decidim-core 0.29.4 → 0.29.5

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity/show.erb +6 -6
  3. data/app/cells/decidim/content_blocks/participatory_space_main_data/title.erb +11 -2
  4. data/app/cells/decidim/footer_topics/show.erb +2 -2
  5. data/app/cells/decidim/group_admins/show.erb +3 -1
  6. data/app/cells/decidim/group_members/show.erb +6 -2
  7. data/app/cells/decidim/images_panel/show.erb +5 -2
  8. data/app/cells/decidim/report_button/flag_modal.erb +11 -9
  9. data/app/cells/decidim/report_user_button/flag_modal.erb +11 -10
  10. data/app/cells/decidim/upload_modal/files.erb +4 -4
  11. data/app/cells/decidim/upload_modal_cell.rb +5 -3
  12. data/app/commands/decidim/amendable/accept.rb +2 -1
  13. data/app/controllers/concerns/decidim/headers/browser_feature_permissions.rb +50 -0
  14. data/app/controllers/decidim/amendments_controller.rb +3 -3
  15. data/app/controllers/decidim/application_controller.rb +1 -0
  16. data/app/helpers/decidim/amendments_helper.rb +2 -1
  17. data/app/helpers/decidim/filters_helper.rb +25 -0
  18. data/app/helpers/decidim/layout_helper.rb +6 -0
  19. data/app/packs/images/decidim/default-avatar.svg +1 -1
  20. data/app/packs/src/decidim/callout.js +13 -8
  21. data/app/packs/src/decidim/confirm.js +79 -59
  22. data/app/packs/src/decidim/datepicker/generate_datepicker.js +2 -0
  23. data/app/packs/src/decidim/datepicker/generate_timepicker.js +2 -0
  24. data/app/packs/src/decidim/direct_uploads/upload_field.js +3 -4
  25. data/app/packs/src/decidim/direct_uploads/upload_modal.js +8 -9
  26. data/app/packs/src/decidim/dropdown_menu.js +18 -0
  27. data/app/packs/src/decidim/editor/common/suggestion.js +11 -1
  28. data/app/packs/src/decidim/form_remote.js +1 -1
  29. data/app/packs/src/decidim/impersonation.js +1 -1
  30. data/app/packs/src/decidim/index.js +5 -1
  31. data/app/packs/src/decidim/session_timeouter.js +1 -1
  32. data/app/packs/src/decidim/utilities/dom.js +148 -0
  33. data/app/packs/stylesheets/decidim/_activity.scss +4 -4
  34. data/app/packs/stylesheets/decidim/_cards.scss +5 -1
  35. data/app/packs/stylesheets/decidim/_filters.scss +1 -1
  36. data/app/packs/stylesheets/decidim/_header.scss +11 -3
  37. data/app/packs/stylesheets/decidim/_layout.scss +2 -2
  38. data/app/packs/stylesheets/decidim/_modal.scss +1 -5
  39. data/app/packs/stylesheets/decidim/_modal_update.scss +5 -1
  40. data/app/permissions/decidim/permissions.rb +13 -1
  41. data/app/views/decidim/errors/internal_server_error.html.erb +1 -1
  42. data/app/views/decidim/errors/not_found.html.erb +1 -1
  43. data/app/views/decidim/newsletters/unsubscribe.html.erb +16 -4
  44. data/app/views/decidim/searches/_filters.html.erb +48 -13
  45. data/app/views/decidim/shared/_component_announcement.html.erb +1 -1
  46. data/app/views/decidim/shared/_confirm_modal.html.erb +3 -5
  47. data/app/views/decidim/shared/_filters.html.erb +6 -4
  48. data/app/views/layouts/decidim/_js_configuration.html.erb +1 -0
  49. data/app/views/layouts/decidim/footer/_main.html.erb +1 -1
  50. data/app/views/layouts/decidim/footer/_main_intro.html.erb +1 -1
  51. data/app/views/layouts/decidim/footer/_mini.html.erb +2 -2
  52. data/app/views/layouts/decidim/header/_main.html.erb +2 -2
  53. data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +6 -0
  54. data/app/views/layouts/decidim/header/_main_links_dropdown.html.erb +2 -0
  55. data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +1 -1
  56. data/app/views/layouts/decidim/header/_mobile_language_choose.html.erb +1 -1
  57. data/config/locales/ar.yml +0 -3
  58. data/config/locales/bg-BG.yml +2 -2
  59. data/config/locales/bg.yml +0 -5
  60. data/config/locales/ca-IT.yml +29 -5
  61. data/config/locales/ca.yml +29 -5
  62. data/config/locales/cs.yml +15 -6
  63. data/config/locales/de.yml +47 -23
  64. data/config/locales/el.yml +0 -4
  65. data/config/locales/en.yml +29 -5
  66. data/config/locales/es-MX.yml +29 -5
  67. data/config/locales/es-PY.yml +29 -5
  68. data/config/locales/es.yml +29 -5
  69. data/config/locales/eu.yml +63 -39
  70. data/config/locales/fi-plain.yml +58 -5
  71. data/config/locales/fi.yml +59 -6
  72. data/config/locales/fr-CA.yml +29 -5
  73. data/config/locales/fr-LU.yml +3 -3
  74. data/config/locales/fr.yml +29 -5
  75. data/config/locales/gl.yml +0 -3
  76. data/config/locales/hu.yml +0 -5
  77. data/config/locales/id-ID.yml +0 -3
  78. data/config/locales/is-IS.yml +0 -1
  79. data/config/locales/it.yml +124 -3
  80. data/config/locales/ja.yml +29 -5
  81. data/config/locales/lb-LU.yml +2 -2
  82. data/config/locales/lb.yml +0 -3
  83. data/config/locales/lt.yml +0 -5
  84. data/config/locales/lv.yml +0 -3
  85. data/config/locales/nl.yml +0 -3
  86. data/config/locales/no.yml +0 -3
  87. data/config/locales/pl.yml +0 -5
  88. data/config/locales/pt-BR.yml +1 -4
  89. data/config/locales/pt.yml +0 -3
  90. data/config/locales/ro-RO.yml +0 -4
  91. data/config/locales/ru.yml +0 -3
  92. data/config/locales/sk-SK.yml +3 -3
  93. data/config/locales/sk.yml +2 -3
  94. data/config/locales/sv.yml +28 -4
  95. data/config/locales/tr-TR.yml +0 -3
  96. data/config/locales/uk.yml +0 -2
  97. data/config/locales/zh-CN.yml +0 -3
  98. data/config/locales/zh-TW.yml +0 -5
  99. data/lib/decidim/assets/tailwind/tailwind.config.js.erb +2 -1
  100. data/lib/decidim/core/test/factories.rb +2 -2
  101. data/lib/decidim/core/test/shared_examples/announcements_examples.rb +4 -0
  102. data/lib/decidim/core/version.rb +1 -1
  103. data/lib/decidim/form_builder.rb +14 -0
  104. metadata +9 -6
@@ -1,5 +1,4 @@
1
1
  import UploadModal from "src/decidim/direct_uploads/upload_modal";
2
- import { truncateFilename } from "src/decidim/direct_uploads/upload_utility";
3
2
  import { escapeHtml, escapeQuotes } from "src/decidim/utilities/text";
4
3
 
5
4
  const updateModalTitle = (modal) => {
@@ -30,7 +29,7 @@ const updateActiveUploads = (modal) => {
30
29
  const [removeFiles, addFiles] = [modal.items.filter(({ removable }) => removable), modal.items.filter(({ removable }) => !removable)]
31
30
 
32
31
  addFiles.forEach((file, ix) => {
33
- let title = truncateFilename(file.name, 19)
32
+ let title = file.name
34
33
 
35
34
  let hidden = ""
36
35
  if (file.hiddenField) {
@@ -79,8 +78,8 @@ const updateActiveUploads = (modal) => {
79
78
 
80
79
  const template = `
81
80
  <div ${attachmentIdOrHiddenField} data-filename="${escapeQuotes(file.name)}" data-title="${escapeQuotes(title)}">
82
- ${(/image/).test(file.type) && `<div><img src="data:," alt="${escapeQuotes(file.name)}" /></div>` || ""}
83
- <span>${escapeHtml(title)} (${escapeHtml(truncateFilename(file.name))})</span>
81
+ ${(/image/).test(file.type) && "<div><img src=\"data:,\" role=\"presentation\" /></div>" || ""}
82
+ <span>${escapeHtml(title)}</span>
84
83
  ${hidden}
85
84
  </div>
86
85
  `
@@ -1,6 +1,5 @@
1
1
  import { Uploader } from "src/decidim/direct_uploads/uploader";
2
2
  import icon from "src/decidim/icon";
3
- import { truncateFilename } from "src/decidim/direct_uploads/upload_utility";
4
3
  import { escapeHtml, escapeQuotes } from "src/decidim/utilities/text";
5
4
 
6
5
  const STATUS = {
@@ -42,7 +41,7 @@ export default class UploadModal {
42
41
 
43
42
  this.emptyItems = this.modal.querySelector("[data-dropzone-no-items]");
44
43
  this.uploadItems = this.modal.querySelector("[data-dropzone-items]");
45
- this.input = this.dropZone.querySelector("input");
44
+ this.input = this.dropZone.querySelector("input[type=file]");
46
45
  this.items = []
47
46
 
48
47
  this.attachmentCounter = 0;
@@ -171,29 +170,29 @@ export default class UploadModal {
171
170
 
172
171
  createUploadItem(file, errors, opts = {}) {
173
172
  const okTemplate = `
174
- <img src="data:," alt="${escapeQuotes(file.name)}" />
175
- <span>${escapeHtml(truncateFilename(file.name))}</span>
173
+ <img src="data:,", role="presentation" />
174
+ <span class="upload-modal__span">${escapeHtml(file.name)}</span>
176
175
  `
177
176
 
178
177
  const errorTemplate = `
179
178
  <div>${icon("error-warning-line")}</div>
180
179
  <div>
181
- <span>${escapeHtml(truncateFilename(file.name))}</span>
180
+ <span class="upload-modal__span">${escapeHtml(file.name)}</span>
182
181
  <span>${this.locales.validation_error}</span>
183
182
  <ul>${errors.map((error) => `<li>${error}</li>`).join("\n")}</ul>
184
183
  </div>
185
184
  `
186
185
 
187
186
  const titleTemplate = `
188
- <img src="data:," alt="${escapeQuotes(file.name)}" />
187
+ <img src="data:," role="presentation" />
189
188
  <div>
190
189
  <div>
191
190
  <label>${this.locales.filename}</label>
192
- <span>${escapeHtml(truncateFilename(file.name))}</span>
191
+ <span class="upload-modal__span">${escapeHtml(file.name)}</span>
193
192
  </div>
194
193
  <div>
195
- <label>${this.locales.title}</label>
196
- <input class="sm" type="text" value="${escapeQuotes(opts.title || truncateFilename(file.name))}" />
194
+ <label for="${file.name}">${this.locales.title}</label>
195
+ <input class="sm" type="text" value="${escapeQuotes(opts.title || file.name)}" id="${file.name}" />
197
196
  </div>
198
197
  </div>
199
198
  `
@@ -0,0 +1,18 @@
1
+ // changes the value "menu" of role attribute set by a11y on div dropdown-menu-account and
2
+ // dropdown-menu-account-mobile which are inappropriate for accessibility
3
+ document.addEventListener("DOMContentLoaded", () => {
4
+ const dropdownDiv = document.querySelector("#dropdown-menu-account");
5
+ const dropdownMobileDiv = document.querySelector("#dropdown-menu-account-mobile");
6
+ if (dropdownDiv) {
7
+ setTimeout(() => {
8
+ dropdownDiv.setAttribute("role", "dialog")
9
+ dropdownMobileDiv.setAttribute("role", "dialog")
10
+ }, 300)
11
+ }
12
+ const triggerButtonMobile = document.querySelector("#dropdown-trigger-links-mobile");
13
+ if (triggerButtonMobile) {
14
+ triggerButtonMobile.addEventListener("click", () => {
15
+ dropdownMobileDiv.setAttribute("aria-modal", "true")
16
+ })
17
+ }
18
+ });
@@ -98,7 +98,17 @@ export const createSuggestionRenderer = (node, { itemConverter } = {}) => () =>
98
98
  suggestionItem.textContent = label;
99
99
  suggestion.append(suggestionItem);
100
100
 
101
- suggestionItem.addEventListener("click", () => selectItem(idx));
101
+ suggestionItem.addEventListener("click", (ev) => {
102
+ ev.preventDefault();
103
+
104
+ if (currentRange) {
105
+ // Increase the current range by 1 since the ENTER key would do the
106
+ // same when doing the selection through keyboard.
107
+ currentRange.to += 1;
108
+ }
109
+
110
+ selectItem(idx);
111
+ });
102
112
  });
103
113
  }
104
114
 
@@ -1,4 +1,4 @@
1
- import Rails from "@rails/ujs";
1
+ const { Rails } = window;
2
2
 
3
3
  // Make the remote form submit buttons disabled when the form is being
4
4
  // submitted to avoid multiple submits.
@@ -15,7 +15,7 @@ $(() => {
15
15
  }, 1000);
16
16
 
17
17
  // Prevent reload when page is already unloading, otherwise it may cause infinite reloads.
18
- window.addEventListener("beforeunload", () => {
18
+ window.addEventListener("pagehide", () => {
19
19
  clearInterval(exitInterval);
20
20
  return;
21
21
  });
@@ -41,7 +41,6 @@ import "src/decidim/vizzs"
41
41
  import "src/decidim/responsive_horizontal_tabs"
42
42
  import "src/decidim/security/selfxss_warning"
43
43
  import "src/decidim/session_timeouter"
44
- import "src/decidim/confirm"
45
44
  import "src/decidim/results_listing"
46
45
  import "src/decidim/impersonation"
47
46
  import "src/decidim/gallery"
@@ -51,8 +50,10 @@ import "src/decidim/abide_form_validator_fixer"
51
50
  import "src/decidim/sw"
52
51
  import "src/decidim/sticky_header"
53
52
  import "src/decidim/attachments"
53
+ import "src/decidim/dropdown_menu"
54
54
 
55
55
  // local deps that require initialization
56
+ import ConfirmDialog, { initializeConfirm } from "src/decidim/confirm"
56
57
  import formDatePicker from "src/decidim/datepicker/form_datepicker"
57
58
  import Configuration from "src/decidim/configuration"
58
59
  import ExternalLink from "src/decidim/external_link"
@@ -91,6 +92,7 @@ window.Decidim = window.Decidim || {
91
92
  addInputEmoji,
92
93
  EmojiButton,
93
94
  Dialogs,
95
+ ConfirmDialog,
94
96
  announceForScreenReader
95
97
  };
96
98
 
@@ -123,6 +125,8 @@ window.initFoundation = (element) => {
123
125
  });
124
126
  };
125
127
 
128
+ // Confirm initialization needs to happen before Rails.start()
129
+ initializeConfirm();
126
130
  Rails.start()
127
131
 
128
132
  /**
@@ -127,7 +127,7 @@ $(() => {
127
127
  setTimer(timeoutInSeconds);
128
128
  });
129
129
 
130
- window.addEventListener("beforeunload", () => {
130
+ window.addEventListener("pagehide", () => {
131
131
  clearInterval(exitInterval);
132
132
  return;
133
133
  });
@@ -0,0 +1,148 @@
1
+ import confirmAction from "src/decidim/confirm"
2
+ import { getMessages } from "src/decidim/i18n"
3
+
4
+ const { Rails } = window;
5
+
6
+ const createUnloadPreventer = () => {
7
+ const preventUnloadConditions = [];
8
+
9
+ const confirmMessage = getMessages("confirmUnload") || "Are you sure you want to leave this page?";
10
+
11
+ const canUnload = (event) => !preventUnloadConditions.some((condition) => condition(event));
12
+
13
+ // TLDR:
14
+ // The beforeunload event does not work during tests due to the deprecation of
15
+ // the unload event and ChromeDriver automatically accepting these dialogs.
16
+ // ---
17
+ //
18
+ // Even when there are custom listeners on links and forms, the beforeunload
19
+ // event is to ensure that the user does not accidentally reload the page or
20
+ // close the browser or the tab. Note that this does not work during the tests
21
+ // with ChromeDriver due to the deprecation of the unload event and
22
+ // ChromeDriver automatically accepting these dialogs. For the time being,
23
+ // this should work when a real user interacts with the browser along with the
24
+ // "Permissions-Policy" header set by the backend. For more information about
25
+ // the header, see Decidim::Headers::BrowserFeaturePermissions).
26
+ const unloadListener = (event) => {
27
+ if (canUnload(event)) {
28
+ return;
29
+ }
30
+
31
+ // According to:
32
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
33
+ //
34
+ // > [...] best practice is to trigger the dialog by invoking
35
+ // > preventDefault() on the event object, while also setting returnValue to
36
+ // > support legacy cases.
37
+ event.preventDefault();
38
+ event.returnValue = true;
39
+ };
40
+
41
+ // The beforeunload event listener has to be registered AFTER a user
42
+ // interaction which is why it is wrapped around the next click event that
43
+ // happens after the first unload listener was registered. Otherwise it might
44
+ // not work due to the deprecation of the unload APIs in Chromium based
45
+ // browsers and possibly in the web standards in the future.
46
+ //
47
+ // According to:
48
+ // https://developer.chrome.com/docs/web-platform/page-lifecycle-api#the_beforeunload_event
49
+ //
50
+ // > Never add a beforeunload listener unconditionally or use it as an
51
+ // > end-of-session signal. Only add it when a user has unsaved work, and
52
+ // > remove it as soon as that work has been saved.
53
+ const registerBeforeUnload = () => {
54
+ window.removeEventListener("click", registerBeforeUnload);
55
+ window.addEventListener("beforeunload", unloadListener);
56
+ };
57
+
58
+ const disableBeforeUnload = () => {
59
+ window.removeEventListener("click", registerBeforeUnload);
60
+ window.removeEventListener("beforeunload", unloadListener);
61
+ };
62
+
63
+ const linkClickListener = (ev) => {
64
+ const link = ev.target?.closest("a");
65
+ if (!link) {
66
+ return;
67
+ }
68
+
69
+ if (canUnload(ev)) {
70
+ disableBeforeUnload();
71
+ document.removeEventListener("click", linkClickListener);
72
+ return;
73
+ }
74
+
75
+ window.exitUrl = link.href;
76
+
77
+ ev.preventDefault();
78
+ ev.stopPropagation();
79
+
80
+ confirmAction(confirmMessage, link).then((answer) => {
81
+ if (!answer) {
82
+ return;
83
+ }
84
+
85
+ disableBeforeUnload();
86
+ document.removeEventListener("click", linkClickListener);
87
+ link.click();
88
+ });
89
+ };
90
+
91
+ const formSubmitListener = (ev) => {
92
+ const source = ev.target?.closest("form");
93
+ if (!source) {
94
+ return;
95
+ }
96
+
97
+ if (canUnload(ev)) {
98
+ disableBeforeUnload();
99
+ document.removeEventListener("submit", formSubmitListener);
100
+ return;
101
+ }
102
+
103
+ const button = source.closest(Rails.formSubmitSelector);
104
+ if (!button) {
105
+ return;
106
+ }
107
+
108
+ ev.preventDefault();
109
+ ev.stopImmediatePropagation();
110
+ ev.stopPropagation();
111
+
112
+ confirmAction(confirmMessage, button).then((answer) => {
113
+ if (!answer) {
114
+ return;
115
+ }
116
+
117
+ disableBeforeUnload();
118
+ document.removeEventListener("submit", formSubmitListener);
119
+ source.submit();
120
+ });
121
+ };
122
+
123
+ const registerPreventUnloadListeners = () => {
124
+ window.addEventListener("click", registerBeforeUnload);
125
+ document.addEventListener("click", linkClickListener);
126
+ document.addEventListener("submit", formSubmitListener);
127
+ };
128
+
129
+ return {
130
+ addPreventCondition: (condition) => {
131
+ if (typeof condition !== "function") {
132
+ return;
133
+ }
134
+
135
+ if (preventUnloadConditions.length < 1) {
136
+ // The unload listeners are global, so only the first call to this
137
+ // function should result to registering these listeners.
138
+ registerPreventUnloadListeners();
139
+ }
140
+
141
+ preventUnloadConditions.push(condition);
142
+ }
143
+ };
144
+ };
145
+
146
+ const unloadPreventer = createUnloadPreventer();
147
+
148
+ export const preventUnload = (condition) => unloadPreventer.addPreventCondition(condition);
@@ -12,12 +12,12 @@
12
12
  &__content {
13
13
  @apply col-span-2 md:col-span-1 order-first md:order-none space-y-2;
14
14
 
15
- > span:first-child {
16
- span {
17
- @apply text-gray-2 text-sm;
15
+ > div:first-child {
16
+ p {
17
+ @apply text-gray-2 text-sm inline-block;
18
18
 
19
19
  svg {
20
- @apply text-gray fill-current;
20
+ @apply text-gray fill-current inline;
21
21
  }
22
22
  }
23
23
 
@@ -159,11 +159,15 @@
159
159
  /* shared styles */
160
160
  &__highlight-metadata,
161
161
  &__grid-metadata {
162
- @apply mt-auto flex items-center justify-between flex-wrap w-full text-sm text-gray-2 [&>*]:flex [&>*]:items-center [&>*]:gap-1 first:[&>*]:w-4/5;
162
+ @apply mt-auto flex items-center justify-between flex-wrap text-sm text-gray-2 [&>*]:flex [&>*]:items-center [&>*]:gap-1 first:[&>*]:flex-none;
163
163
 
164
164
  svg {
165
165
  @apply flex-none text-gray fill-current;
166
166
  }
167
+
168
+ [data-author] {
169
+ @apply flex first:[&>*]:max-w-40 -mr-8;
170
+ }
167
171
  }
168
172
 
169
173
  /* shared styles */
@@ -89,7 +89,7 @@
89
89
  }
90
90
 
91
91
  span {
92
- @apply text-sm text-gray-2 truncate whitespace-nowrap first-letter:uppercase;
92
+ @apply text-sm text-gray-2 first-letter:uppercase;
93
93
  }
94
94
 
95
95
  [data-controls*="panel"] {
@@ -61,7 +61,7 @@ header {
61
61
  @apply hidden md:block col-span-2 col-start-5 xl:col-start-4;
62
62
 
63
63
  form {
64
- @apply block relative rounded text-md bg-background;
64
+ @apply block relative rounded text-md border border-neutral-200 outline outline-1 outline-transparent rounded bg-background-2 leading-relaxed;
65
65
  }
66
66
 
67
67
  input[type="text"] {
@@ -69,7 +69,7 @@ header {
69
69
  }
70
70
 
71
71
  button[type="submit"] {
72
- @apply absolute ltr:right-2 rtl:left-2 inset-y-2 text-secondary;
72
+ @apply absolute ltr:right-2 rtl:left-2 inset-y-0 text-secondary px-2;
73
73
  }
74
74
  }
75
75
 
@@ -404,7 +404,7 @@ header {
404
404
  @apply w-full px-4;
405
405
 
406
406
  &-menu {
407
- @apply w-full md:w-1/2 mt-0 grid md:grid-cols-2 gap-x-6 text-secondary;
407
+ @apply w-full md:w-[100%] mt-0 grid md:grid-cols-2 gap-x-6 text-secondary;
408
408
 
409
409
  > * {
410
410
  @apply py-3 md:py-3.5 border-b last:border-0 border-gray-3;
@@ -431,6 +431,14 @@ header {
431
431
 
432
432
  &__bottom {
433
433
  @apply hidden md:flex;
434
+
435
+ &-right {
436
+ @apply mr-2 mb-2;
437
+ }
438
+
439
+ &-left {
440
+ @apply mr-2 mt-1;
441
+ }
434
442
  }
435
443
 
436
444
  &__title {
@@ -2,11 +2,11 @@
2
2
  @apply flex flex-col min-h-screen;
3
3
 
4
4
  &__skip {
5
- @apply absolute z-10 left-0 -translate-x-full py-1 px-4 bg-primary rounded-br-lg text-white cursor-pointer transition focus:translate-x-0;
5
+ @apply absolute z-[9999] left-0 -translate-x-full py-1 px-4 bg-primary rounded-br-lg text-white cursor-pointer transition focus:translate-x-0;
6
6
  }
7
7
 
8
8
  [data-content] {
9
- @apply relative flex flex-col;
9
+ @apply relative flex flex-col flex-1;
10
10
  }
11
11
  }
12
12
 
@@ -18,15 +18,11 @@
18
18
  }
19
19
 
20
20
  [data-dialog-container] {
21
- @apply grid grid-cols-[auto_1fr] items-start md:items-center gap-2 text-left;
21
+ @apply grid grid-rows-[auto_1fr] items-start md:items-center gap-2 text-left;
22
22
 
23
23
  > svg {
24
24
  @apply w-6 h-6 text-gray fill-current flex-none;
25
25
  }
26
-
27
- > :last-child {
28
- @apply col-span-2 md:col-span-1 md:col-start-2;
29
- }
30
26
  }
31
27
 
32
28
  [data-dialog-title] {
@@ -132,8 +132,12 @@
132
132
  }
133
133
 
134
134
  span {
135
- @apply text-sm text-gray-2 mx-auto;
135
+ @apply text-sm text-gray-2 mx-auto w-full break-all mb-2;
136
136
  }
137
137
  }
138
138
  }
139
+
140
+ &__span {
141
+ @apply w-full break-all mb-2;
142
+ }
139
143
  }
@@ -106,7 +106,19 @@ module Decidim
106
106
  return allow! if component.current_settings.amendment_creation_enabled
107
107
  when :accept,
108
108
  :reject
109
- return allow! if component.current_settings.amendment_reaction_enabled
109
+ return disallow! unless component.current_settings.amendment_reaction_enabled
110
+
111
+ amendable = context.fetch(:amendable, nil)
112
+
113
+ if amendable.respond_to?(:official?) && amendable.official?
114
+ return allow! if user.admin?
115
+
116
+ return disallow!
117
+ end
118
+
119
+ return disallow! unless amendable.authored_by?(user)
120
+
121
+ return allow!
110
122
  when :promote
111
123
  return allow! if component.current_settings.amendment_promotion_enabled
112
124
  end
@@ -1,6 +1,6 @@
1
1
  <%= render layout: "layouts/decidim/shared/layout_center" do %>
2
2
 
3
- <div class="place-self-center">
3
+ <div class="flex flex-col items-center">
4
4
  <div class="flex justify-center">
5
5
  <h1 class="title-decorator my-12"><%= t("title", scope: "decidim.errors.internal_server_error") %></h1>
6
6
  </div>
@@ -1,6 +1,6 @@
1
1
  <%= render layout: "layouts/decidim/shared/layout_center" do %>
2
2
 
3
- <div class="place-self-center">
3
+ <div class="flex flex-col items-center">
4
4
  <div class="flex justify-center">
5
5
  <h1 class="title-decorator my-12"><%= t("title", scope: "decidim.errors.not_found") %></h1>
6
6
  </div>
@@ -1,4 +1,16 @@
1
- <div class="row">
2
- <h4><%== t ".unsubscribe" %></h4>
3
- <p><%== t ".check_subscription", link: decidim.notifications_settings_path %></p>
4
- </div>
1
+ <main class="layout-1col cols-6">
2
+ <div class="text-center py-12">
3
+ <h1 class="h1 decorator inline-block text-left"><%= t ".unsubscribe" %></h1>
4
+ </div>
5
+ <div class="page__container">
6
+ <div class="editor-content">
7
+ <div class="rich-text-display">
8
+ <%= t ".subscription_preferences", organization_name: translated_attribute(current_organization.name) %>
9
+ </div>
10
+ <br>
11
+ <div class="rich-text-display">
12
+ <%= t ".check_subscription_html", link: decidim.notifications_settings_path %>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ </main>
@@ -12,35 +12,70 @@
12
12
  <%= icon "arrow-down-s-line", class: "w-8 h-8 flex-none text-secondary fill-current" %>
13
13
  <%= icon "arrow-up-s-line", class: "w-8 h-8 flex-none text-secondary fill-current" %>
14
14
  </button>
15
- <div id="dropdown-menu-search" aria-hidden="true">
16
- <div>
17
- <%= link_to main_search_path, class: "filter#{" is-active" if params.dig(:filter, :with_resource_type) == nil}" do %>
15
+ <ul id="dropdown-menu-search" aria-hidden="true">
16
+ <li>
17
+ <%= link_to main_search_path, class: "filter#{" is-active" if params.dig(:filter, :with_resource_type) == nil}", "aria-current": params.dig(:filter, :with_resource_type) == nil ? "true" : "" do %>
18
18
  <%= resource_type_icon("all") %>
19
19
  <span><%= t("all", scope: "decidim.searches.filters.state") %></span>
20
20
  <span class="label ml-auto"><%= @results_count %></span>
21
21
  <% end %>
22
- </div>
22
+ </li>
23
23
  <% @blocks.each do |elements| %>
24
- <div>
25
- <% elements.each do |type, results| %>
26
- <div>
24
+ <li>
25
+ <ul>
26
+ <% elements.each do |type, results| %>
27
27
  <% if results[:count].positive? %>
28
- <%= link_to search_path_by_resource_type(type), class: "filter#{" is-active" if params.dig(:filter, :with_resource_type) == type}" do %>
28
+ <li>
29
+ <%= link_to search_path_by_resource_type(type), class: "filter#{" is-active" if params.dig(:filter, :with_resource_type) == type}", "aria-current": params.dig(:filter, :with_resource_type) == type ? "true" : "" do %>
29
30
  <%= resource_type_icon(type) %>
30
31
  <span><%= searchable_resource_human_name(type) %></span>
31
32
  <span class="label ml-auto"><%= results[:count] %></span>
32
33
  <% end %>
34
+ </li>
33
35
  <% else %>
34
- <%= content_tag :div, class: "filter#{" is-empty" if results[:count].zero?}" do %>
36
+ <%= content_tag :li, class: "filter#{" is-empty" if results[:count].zero?}" do %>
35
37
  <%= resource_type_icon(type) %>
36
38
  <span><%= searchable_resource_human_name(type) %></span>
37
39
  <span class="label ml-auto"><%= results[:count] %></span>
38
40
  <% end %>
39
41
  <% end %>
40
- </div>
41
- <% end %>
42
- </div>
42
+ <% end %>
43
+ </ul>
44
+ </li>
43
45
  <% end %>
44
- </div>
46
+ </ul>
45
47
  </div>
46
48
  </nav>
49
+ <script type="text/javascript">
50
+ const button = document.querySelector('#dropdown-trigger-search');
51
+ const arrowDown = button.querySelector('svg:first-of-type');
52
+ const arrowUp = button.querySelector('svg:last-of-type');
53
+ const list = document.querySelector('#dropdown-menu-search');
54
+ // the arrow is up when user arrives on the page, with the list displayed
55
+ arrowUp.addEventListener('click', function(){
56
+ setTimeout(() => {
57
+ button.setAttribute('aria-expanded', 'false');
58
+ list.style.display = "none";
59
+ }, 300)
60
+ })
61
+ arrowDown.addEventListener('click', function(){
62
+ setTimeout(() => {
63
+ button.setAttribute('aria-expanded', 'true');
64
+ list.style.display = "block";
65
+ }, 300)
66
+ })
67
+ // 32 is code for space bar and 13 is code for enter
68
+ button.addEventListener('keydown', function(e){
69
+ if ((e.keyCode === 13 || e.keyCode === 32) && button.getAttribute('aria-expanded') === 'true') {
70
+ setTimeout(() => {
71
+ button.setAttribute('aria-expanded', 'false');
72
+ list.style.display = "none";
73
+ }, 300)
74
+ } else if ((e.keyCode === 13 || e.keyCode === 32) && button.getAttribute('aria-expanded') === 'false'){
75
+ setTimeout(() => {
76
+ button.setAttribute('aria-expanded', 'true');
77
+ list.style.display = "block";
78
+ }, 300)
79
+ }
80
+ })
81
+ </script>
@@ -1,5 +1,5 @@
1
1
  <% announcement = translated_attribute(current_settings.announcement).presence || translated_attribute(component_settings.announcement).presence %>
2
- <% if announcement %>
2
+ <% if strip_tags(announcement).present? %>
3
3
  <section class="layout-main__section">
4
4
  <%= cell("decidim/announcement", announcement, local_assigns.merge(callout_class: "editor-content")) %>
5
5
  </section>
@@ -1,16 +1,14 @@
1
1
  <%= decidim_modal id: "confirm-modal" do %>
2
2
  <div data-dialog-container>
3
- <%= icon "delete-bin-line" %>
4
3
  <h2 class="h2" data-dialog-title id="dialog-title-confirm-modal"><%= t("title", scope: "decidim.shared.confirm_modal") %></h2>
5
-
6
4
  <div data-confirm-modal-content></div>
7
5
  </div>
8
6
 
9
- <div data-dialog-actions>
10
- <button class="button button__lg button__transparent-secondary" data-confirm-cancel data-dialog-close="confirm-modal">
7
+ <div data-dialog-actions class="flex flex-col-reverse md:flex-row">
8
+ <button class="button button__lg button__transparent-secondary w-full md:w-auto" data-confirm-cancel data-dialog-close="confirm-modal">
11
9
  <span><%= t("cancel", scope: "decidim.shared.confirm_modal") %></span>
12
10
  </button>
13
- <button class="button button__lg button__secondary" data-confirm-ok>
11
+ <button class="button button__lg button__secondary w-full md:w-auto" data-confirm-ok>
14
12
  <span><%= t("ok", scope: "decidim.shared.confirm_modal") %></span>
15
13
  </button>
16
14
  </div>