decidim-core 0.30.1 → 0.30.3

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/app/cells/decidim/activity/show.erb +6 -6
  3. data/app/cells/decidim/author/badge.erb +6 -0
  4. data/app/cells/decidim/author/show.erb +5 -1
  5. data/app/cells/decidim/author_cell.rb +10 -0
  6. data/app/cells/decidim/content_blocks/participatory_space_main_data/title.erb +11 -2
  7. data/app/cells/decidim/footer_topics/show.erb +2 -2
  8. data/app/cells/decidim/group_admins/show.erb +3 -1
  9. data/app/cells/decidim/group_members/show.erb +6 -2
  10. data/app/cells/decidim/images_panel/show.erb +5 -2
  11. data/app/cells/decidim/profile/avatar.erb +0 -2
  12. data/app/cells/decidim/profile/badge.erb +3 -3
  13. data/app/cells/decidim/profile/details.erb +2 -1
  14. data/app/cells/decidim/report_button/flag_modal.erb +11 -9
  15. data/app/cells/decidim/report_user_button/flag_modal.erb +11 -10
  16. data/app/cells/decidim/upload_modal/files.erb +4 -4
  17. data/app/cells/decidim/upload_modal_cell.rb +5 -3
  18. data/app/cells/decidim/user_activity_cell.rb +6 -1
  19. data/app/commands/decidim/amendable/accept.rb +2 -1
  20. data/app/commands/decidim/create_omniauth_registration.rb +1 -1
  21. data/app/controllers/decidim/amendments_controller.rb +3 -3
  22. data/app/forms/decidim/user_group_form.rb +2 -0
  23. data/app/helpers/decidim/amendments_helper.rb +2 -1
  24. data/app/helpers/decidim/filters_helper.rb +25 -0
  25. data/app/helpers/decidim/layout_helper.rb +6 -0
  26. data/app/helpers/decidim/map_helper.rb +1 -1
  27. data/app/helpers/decidim/menu_helper.rb +8 -2
  28. data/app/packs/images/decidim/default-avatar.svg +1 -1
  29. data/app/packs/src/decidim/callout.js +13 -8
  30. data/app/packs/src/decidim/confirm.js +15 -3
  31. data/app/packs/src/decidim/datepicker/generate_datepicker.js +2 -0
  32. data/app/packs/src/decidim/datepicker/generate_timepicker.js +2 -0
  33. data/app/packs/src/decidim/direct_uploads/upload_field.js +3 -4
  34. data/app/packs/src/decidim/direct_uploads/upload_modal.js +8 -9
  35. data/app/packs/src/decidim/dropdown_menu.js +18 -0
  36. data/app/packs/src/decidim/editor/common/suggestion.js +11 -1
  37. data/app/packs/src/decidim/form_filter.js +6 -0
  38. data/app/packs/src/decidim/index.js +1 -0
  39. data/app/packs/stylesheets/decidim/_activity.scss +4 -4
  40. data/app/packs/stylesheets/decidim/_author.scss +8 -0
  41. data/app/packs/stylesheets/decidim/_cards.scss +10 -2
  42. data/app/packs/stylesheets/decidim/_filters.scss +1 -1
  43. data/app/packs/stylesheets/decidim/_header.scss +11 -3
  44. data/app/packs/stylesheets/decidim/_layout.scss +2 -2
  45. data/app/packs/stylesheets/decidim/_modal.scss +1 -5
  46. data/app/packs/stylesheets/decidim/_modal_update.scss +5 -1
  47. data/app/packs/stylesheets/decidim/_profile.scss +6 -6
  48. data/app/packs/stylesheets/decidim/editor.scss +3 -1
  49. data/app/permissions/decidim/permissions.rb +13 -1
  50. data/app/validators/etiquette_validator.rb +2 -2
  51. data/app/validators/password_validator.rb +3 -1
  52. data/app/validators/translated_etiquette_validator.rb +2 -0
  53. data/app/views/decidim/application/_document.html.erb +2 -2
  54. data/app/views/decidim/errors/internal_server_error.html.erb +1 -1
  55. data/app/views/decidim/errors/not_found.html.erb +1 -1
  56. data/app/views/decidim/newsletters/unsubscribe.html.erb +16 -4
  57. data/app/views/decidim/searches/_filters.html.erb +48 -13
  58. data/app/views/decidim/shared/_component_announcement.html.erb +1 -1
  59. data/app/views/decidim/shared/_confirm_modal.html.erb +3 -5
  60. data/app/views/decidim/shared/_filters.html.erb +7 -5
  61. data/app/views/layouts/decidim/_application.html.erb +1 -1
  62. data/app/views/layouts/decidim/footer/_main.html.erb +1 -1
  63. data/app/views/layouts/decidim/footer/_main_intro.html.erb +1 -1
  64. data/app/views/layouts/decidim/footer/_mini.html.erb +2 -2
  65. data/app/views/layouts/decidim/header/_main.html.erb +2 -2
  66. data/app/views/layouts/decidim/header/_main_links_desktop.html.erb +6 -0
  67. data/app/views/layouts/decidim/header/_main_links_dropdown.html.erb +2 -0
  68. data/app/views/layouts/decidim/header/_main_links_mobile_account.html.erb +1 -1
  69. data/app/views/layouts/decidim/header/_mobile_language_choose.html.erb +1 -1
  70. data/config/locales/ar.yml +0 -3
  71. data/config/locales/bg-BG.yml +2 -2
  72. data/config/locales/bg.yml +0 -5
  73. data/config/locales/ca-IT.yml +36 -13
  74. data/config/locales/ca.yml +36 -13
  75. data/config/locales/cs.yml +20 -6
  76. data/config/locales/de.yml +54 -21
  77. data/config/locales/el.yml +0 -4
  78. data/config/locales/en.yml +28 -5
  79. data/config/locales/es-MX.yml +28 -5
  80. data/config/locales/es-PY.yml +28 -5
  81. data/config/locales/es.yml +28 -5
  82. data/config/locales/eu.yml +101 -78
  83. data/config/locales/fi-plain.yml +76 -5
  84. data/config/locales/fi.yml +77 -6
  85. data/config/locales/fr-CA.yml +28 -5
  86. data/config/locales/fr-LU.yml +3 -3
  87. data/config/locales/fr.yml +28 -5
  88. data/config/locales/gl.yml +0 -3
  89. data/config/locales/hu.yml +0 -5
  90. data/config/locales/id-ID.yml +0 -3
  91. data/config/locales/is-IS.yml +0 -1
  92. data/config/locales/it.yml +165 -3
  93. data/config/locales/ja.yml +37 -5
  94. data/config/locales/lb-LU.yml +2 -2
  95. data/config/locales/lb.yml +0 -3
  96. data/config/locales/lt.yml +0 -5
  97. data/config/locales/lv.yml +0 -3
  98. data/config/locales/nl.yml +0 -3
  99. data/config/locales/no.yml +0 -3
  100. data/config/locales/pl.yml +0 -5
  101. data/config/locales/pt-BR.yml +0 -4
  102. data/config/locales/pt.yml +0 -3
  103. data/config/locales/ro-RO.yml +0 -4
  104. data/config/locales/ru.yml +0 -3
  105. data/config/locales/sk-SK.yml +3 -3
  106. data/config/locales/sk.yml +2 -3
  107. data/config/locales/sv.yml +27 -4
  108. data/config/locales/tr-TR.yml +0 -3
  109. data/config/locales/uk.yml +0 -2
  110. data/config/locales/zh-CN.yml +0 -3
  111. data/config/locales/zh-TW.yml +0 -5
  112. data/lib/decidim/asset_router/storage.rb +8 -8
  113. data/lib/decidim/assets/tailwind/tailwind.config.js.erb +2 -1
  114. data/lib/decidim/core/test/factories.rb +2 -2
  115. data/lib/decidim/core/test/shared_examples/announcements_examples.rb +4 -0
  116. data/lib/decidim/core/test/shared_examples/comments_examples.rb +7 -5
  117. data/lib/decidim/core/version.rb +1 -1
  118. data/lib/decidim/form_builder.rb +15 -1
  119. metadata +8 -6
@@ -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
 
@@ -206,6 +206,12 @@ export default class FormFilterComponent {
206
206
  this.changeEvents = false;
207
207
  this._clearForm();
208
208
 
209
+ // Prevent filtering again on anchor link "Skip to main content", "Skip map", or "Skip to results"
210
+ const filterSkipValues = [...document.querySelectorAll("[data-skip-to-content]")].map((el) => el.hash);
211
+ if (filterSkipValues.includes(window.location.hash)) {
212
+ return;
213
+ }
214
+
209
215
  const filterParams = this._parseLocationFilterValues();
210
216
  const currentOrder = this._parseLocationOrderValue();
211
217
 
@@ -52,6 +52,7 @@ import "src/decidim/sw"
52
52
  import "src/decidim/sticky_header"
53
53
  import "src/decidim/sticky_footer"
54
54
  import "src/decidim/attachments"
55
+ import "src/decidim/dropdown_menu"
55
56
 
56
57
  // local deps that require initialization
57
58
  import ConfirmDialog, { initializeConfirm } from "src/decidim/confirm"
@@ -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
 
@@ -14,6 +14,10 @@
14
14
 
15
15
  &-container {
16
16
  @apply rounded-full overflow-hidden inline-block w-6 h-6 align-top;
17
+
18
+ &:focus-within {
19
+ @apply ring-2 ring-primary;
20
+ }
17
21
  }
18
22
 
19
23
  &-counter {
@@ -29,6 +33,10 @@
29
33
  @apply text-secondary font-semibold;
30
34
  }
31
35
 
36
+ &__badge svg {
37
+ @apply grid overflow-hidden place-items-center bg-primary rounded-full w-4 h-4 text-white fill-current;
38
+ }
39
+
32
40
  &__metadata {
33
41
  @apply flex items-center gap-1 text-gray-2 text-sm;
34
42
 
@@ -59,7 +59,11 @@
59
59
  }
60
60
 
61
61
  &-title {
62
- @apply h4 text-secondary;
62
+ @apply h4;
63
+ }
64
+
65
+ a &-title {
66
+ @apply text-secondary;
63
67
 
64
68
  overflow: hidden;
65
69
  display: -webkit-box;
@@ -163,11 +167,15 @@
163
167
  /* shared styles */
164
168
  &__highlight-metadata,
165
169
  &__grid-metadata {
166
- @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;
170
+ @apply mt-auto flex items-center justify-between flex-wrap text-sm text-gray-2 [&>*]:flex [&>*]:items-center [&>*]:gap-1 first:[&>*]:flex-none;
167
171
 
168
172
  svg {
169
173
  @apply flex-none text-gray fill-current;
170
174
  }
175
+
176
+ [data-author] {
177
+ @apply flex first:[&>*]:max-w-40 -mr-8;
178
+ }
171
179
  }
172
180
 
173
181
  /* 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
  }
@@ -9,18 +9,18 @@
9
9
  &-container {
10
10
  @apply w-24 h-24 relative;
11
11
  }
12
+ }
13
+
14
+ &__details {
15
+ @apply pb-3 space-y-2;
12
16
 
13
17
  &-badge {
14
- @apply absolute top-full right-0 -translate-y-full grid place-items-center w-6 h-6 rounded-full overflow-hidden bg-primary border border-white;
18
+ @apply flex items-center gap-1 text-sm text-gray-2;
15
19
 
16
20
  svg {
17
- @apply w-4 h-4 text-white fill-current;
21
+ @apply w-4 h-4 inline-block bg-primary rounded-full text-white fill-current;
18
22
  }
19
23
  }
20
- }
21
-
22
- &__details {
23
- @apply pb-3 space-y-2;
24
24
 
25
25
  &-data {
26
26
  @apply flex flex-wrap gap-x-6 gap-y-4;
@@ -89,7 +89,9 @@
89
89
  }
90
90
 
91
91
  .ProseMirror {
92
- @apply relative p-2.5 outline-0 min-h-full prose max-w-none prose-headings:first:mt-0 prose-p:first:mt-0 prose-ul:first:mt-0 prose-ol:first:mt-0 prose-blockquote:first:mt-0 prose-pre:first:mt-0;
92
+ @apply relative p-2.5 outline-0 resize-y overflow-hidden prose max-w-none prose-headings:first:mt-0 prose-p:first:mt-0 prose-ul:first:mt-0 prose-ol:first:mt-0 prose-blockquote:first:mt-0 prose-pre:first:mt-0;
93
+
94
+ min-height: inherit;
93
95
 
94
96
  &.ProseMirror-focused,
95
97
  &.dialog-open {
@@ -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
@@ -24,7 +24,7 @@ class EtiquetteValidator < ActiveModel::EachValidator
24
24
  end
25
25
 
26
26
  def validate_caps(record, attribute, value)
27
- number_of_caps = value.scan(/[A-Z]/).length
27
+ number_of_caps = value.scan(/[[:upper:]]/).length
28
28
  return if number_of_caps.zero? || number_of_caps < value.length / 2 # 50%
29
29
 
30
30
  record.errors.add(attribute, options[:message] || :too_much_caps)
@@ -37,7 +37,7 @@ class EtiquetteValidator < ActiveModel::EachValidator
37
37
  end
38
38
 
39
39
  def validate_caps_first(record, attribute, value)
40
- return if value.scan(/\A[a-z]{1}/).empty?
40
+ return if value.scan(/\A[[:lower:]]{1}/).empty?
41
41
 
42
42
  record.errors.add(attribute, options[:message] || :must_start_with_caps)
43
43
  end
@@ -55,7 +55,9 @@ class PasswordValidator < ActiveModel::EachValidator
55
55
  attr_reader :record, :attribute, :value
56
56
 
57
57
  def get_message(reason)
58
- I18n.t "password_validator.#{reason}"
58
+ I18n.t! "password_validator.#{reason}"
59
+ rescue I18n::MissingTranslationData
60
+ I18n.t "password_validator.fallback"
59
61
  end
60
62
 
61
63
  def organization
@@ -6,6 +6,8 @@
6
6
  # validates :my_i18n_field, translated_etiquette: true
7
7
  class TranslatedEtiquetteValidator < EtiquetteValidator
8
8
  def validate_each(record, attribute, _value)
9
+ return unless Decidim.enable_etiquette_validator
10
+
9
11
  translated_attr = "#{attribute}_#{default_locale_for(record)}".gsub("-", "__")
10
12
  translated_value = record.send(translated_attr)
11
13
  return if translated_value.blank?
@@ -1,8 +1,8 @@
1
1
  <div id="<%= dom_id(document) %>">
2
2
  <div class="card__list-content">
3
- <%= link_to document.url, target: "_blank", rel: "noopener noreferrer", class: "card__list-title", title: t("decidim.application.document.download") do %>
3
+ <span class="card__list-title">
4
4
  <%= h attachment_title(document) %>
5
- <% end %>
5
+ </span>
6
6
  <% if document.description.present? %>
7
7
  <div class="card__list-text"><%= decidim_escape_translated(document.description) %></div>
8
8
  <% 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>
@@ -14,18 +14,20 @@
14
14
 
15
15
  <div id="dropdown-menu-filters">
16
16
  <% if local_assigns.has_key?(:skip_to_id) %>
17
- <%= link_to t("skip", scope: "decidim.shared.filter_form_help"), "##{skip_to_id}", class: "filter-skip", role: "menuitem" %>
17
+ <%= link_to t("skip", scope: "decidim.shared.filter_form_help"), "##{skip_to_id}", class: "filter-skip", role: "menuitem", "data-skip-to-content": true %>
18
18
  <% end %>
19
- <p class="filter-help" role="menuitem" aria-disabled="true"><%= t("help", scope: "decidim.shared.filter_form_help") %></p>
19
+
20
+ <p id="filter-help-text" class="filter-help" role="menuitem" aria-disabled="true"><%= t("help", scope: "decidim.shared.filter_form_help") %></p>
20
21
 
21
22
  <% if local_assigns.has_key?(:search_variable) %>
22
23
  <div class="filter-search filter-container" role="menuitem">
23
24
  <%= form.search_field search_variable,
24
25
  label: false,
25
26
  placeholder: search_label,
26
- title: search_label,
27
- "aria-label": search_label %>
28
- <button type="submit" aria-label="<%= search_label %>">
27
+ title: filter_search_label(search_label, skip_to_id),
28
+ "aria-label": filter_search_label(search_label, skip_to_id),
29
+ "aria-describedby": "filter-help-text" %>
30
+ <button type="submit" aria-label="<%= filter_search_label(search_label, skip_to_id) %>">
29
31
  <%= icon "search-line" %>
30
32
  </button>
31
33
  </div>
@@ -8,7 +8,7 @@
8
8
 
9
9
  <body class="text-black text-md form-defaults<%= yield (:body_class) %>">
10
10
  <!--noindex--><!--googleoff: all-->
11
- <%= link_to t("skip_button", scope: "decidim.accessibility"), "#content", class: "layout-container__skip" %>
11
+ <%= link_to t("skip_button", scope: "decidim.accessibility"), "#content", class: "layout-container__skip", "data-skip-to-content": true %>
12
12
  <%= cell("decidim/data_consent", current_organization) %>
13
13
  <!--googleon: all--><!--/noindex-->
14
14
 
@@ -11,7 +11,7 @@
11
11
  <nav class="md:w-1/2 lg:w-auto" role="navigation" aria-label="Legal">
12
12
  <%= render partial: "layouts/decidim/footer/main_legal" %>
13
13
  </nav>
14
- <nav class="w-full md:w-auto md:ml-auto" role="navigation" aria-label="Social media">
14
+ <nav class="w-full md:w-auto md:ml-auto" role="navigation" aria-label="<%= t("layouts.decidim.footer.social_media") %>">
15
15
  <%= render partial: "layouts/decidim/footer/main_social_media_links" %>
16
16
  </nav>
17
17
  <%= render partial: "layouts/decidim/footer/main_language_chooser" %>
@@ -1,6 +1,6 @@
1
1
  <% if current_organization.official_img_footer.attached? %>
2
2
  <%= link_to current_organization.official_url, class: "block mb-6" do %>
3
- <%= image_tag current_organization.attached_uploader(:official_img_footer).url, alt: current_organization_name, class: "max-h-16" %>
3
+ <%= image_tag current_organization.attached_uploader(:official_img_footer).url, alt: t("layouts.decidim.footer.current_organization_img", organization: current_organization_name), class: "max-h-16" %>
4
4
  <% end %>
5
5
  <% end %>
6
6
  <div class="text-sm text-white prose">
@@ -4,9 +4,9 @@
4
4
  <a rel="decidim noopener noreferrer" href="https://decidim.org/" target="_blank" data-external-link="text-only">
5
5
  <%= image_pack_tag("media/images/decidim-logo.svg", alt: t("layouts.decidim.footer.decidim_logo"), class: "max-h-8 block") %>
6
6
  </a>
7
- <div class="text-xs mt-2 [&_a]:underline">
7
+ <p class="text-xs mt-2 [&_a]:underline">
8
8
  <%= t("layouts.decidim.footer.made_with_open_source").html_safe %>
9
- </div>
9
+ </p>
10
10
  </div>
11
11
  <a class="flex gap-1 hover:opacity-50" rel="license noopener noreferrer" href="http://creativecommons.org/licenses/by-sa/4.0/" target="_blank" data-external-link="text-only">
12
12
  <span class="sr-only"><%= t("layouts.decidim.footer.cc_by_license") %></span>
@@ -11,10 +11,10 @@
11
11
  <% if current_user&.ephemeral? %>
12
12
  <%= render partial: "layouts/decidim/header/close_ephemeral_session" %>
13
13
  <% else %>
14
- <div class="main-bar__search">
14
+ <div role="search" class="main-bar__search">
15
15
  <%= render partial: "layouts/decidim/header/main_search" %>
16
16
  </div>
17
- <div class="main-bar__links-desktop">
17
+ <div role="navigation" class="main-bar__links-desktop">
18
18
  <%= render partial: "layouts/decidim/header/main_links_desktop" %>
19
19
  </div>
20
20
  <div class="main-bar__menu-mobile">