decidim-core 0.32.0.rc3 → 0.32.0
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.
- checksums.yaml +4 -4
- data/app/cells/decidim/author_cell.rb +0 -4
- data/app/cells/decidim/content_blocks/highlighted_content_banner_cell.rb +1 -1
- data/app/cells/decidim/content_blocks/highlighted_participatory_spaces_cell.rb +1 -1
- data/app/commands/decidim/destroy_account.rb +12 -1
- data/app/commands/decidim/multiple_attachments_methods.rb +28 -27
- data/app/jobs/decidim/process_inactive_participant_job.rb +0 -7
- data/app/mailers/decidim/delete_user_mailer.rb +14 -0
- data/app/mailers/decidim/participants_account_mailer.rb +0 -16
- data/app/packs/src/decidim/controllers/main_menu/controller.js +33 -0
- data/app/packs/src/decidim/controllers/main_menu/main_menu.test.js +77 -0
- data/app/packs/src/decidim/controllers/mention/controller.js +296 -140
- data/app/packs/src/decidim/controllers/mention/input_mentions.test.js +120 -457
- data/app/packs/src/decidim/controllers/multiple_mentions/controller.js +68 -32
- data/app/packs/src/decidim/controllers/multiple_mentions/input_multiple_mentions.test.js +30 -23
- data/app/packs/src/decidim/editor/common/suggestion.js +3 -1
- data/app/packs/src/decidim/editor/extensions/indent/index.js +9 -0
- data/app/packs/src/decidim/geocoding/reverse_geocoding.js +15 -5
- data/app/packs/src/decidim/geocoding/reverse_geocoding.test.js +197 -0
- data/app/packs/src/decidim/index.js +2 -2
- data/app/packs/stylesheets/decidim/_conversations.scss +14 -0
- data/app/packs/stylesheets/decidim/_dropdown.scss +1 -1
- data/app/packs/stylesheets/decidim/_editor_suggestions.scss +49 -0
- data/app/packs/stylesheets/decidim/_header.scss +12 -8
- data/app/packs/stylesheets/decidim/_tom_select.scss +23 -0
- data/app/packs/stylesheets/decidim/application.scss +2 -0
- data/app/packs/stylesheets/decidim/editor.scss +2 -33
- data/app/packs/stylesheets/decidim/geocoding_addons.scss +10 -2
- data/app/uploaders/decidim/image_uploader.rb +1 -1
- data/app/views/decidim/delete_user_mailer/delete.html.erb +6 -0
- data/app/views/decidim/messaging/conversations/_error_modal.html.erb +11 -19
- data/app/views/decidim/messaging/conversations/error.js.erb +12 -7
- data/app/views/layouts/decidim/header/_menu_breadcrumb_mobile.html.erb +2 -1
- data/config/locales/ar.yml +0 -2
- data/config/locales/bg.yml +0 -2
- data/config/locales/ca-IT.yml +21 -10
- data/config/locales/ca.yml +21 -10
- data/config/locales/cs.yml +10 -9
- data/config/locales/de.yml +4 -13
- data/config/locales/el.yml +0 -1
- data/config/locales/en.yml +20 -9
- data/config/locales/es-MX.yml +20 -9
- data/config/locales/es-PY.yml +20 -9
- data/config/locales/es.yml +20 -9
- data/config/locales/eu.yml +36 -9
- data/config/locales/fi-plain.yml +19 -8
- data/config/locales/fi.yml +19 -8
- data/config/locales/fr-CA.yml +23 -9
- data/config/locales/fr.yml +23 -9
- data/config/locales/hu.yml +0 -2
- data/config/locales/it.yml +0 -2
- data/config/locales/ja.yml +59 -19
- data/config/locales/lb.yml +0 -2
- data/config/locales/lt.yml +0 -2
- data/config/locales/nl.yml +0 -2
- data/config/locales/no.yml +0 -2
- data/config/locales/pl.yml +2 -4
- data/config/locales/pt-BR.yml +2 -11
- data/config/locales/pt.yml +0 -2
- data/config/locales/ro-RO.yml +1 -10
- data/config/locales/sk.yml +1 -10
- data/config/locales/sv.yml +0 -9
- data/config/locales/tr-TR.yml +0 -2
- data/config/locales/zh-CN.yml +0 -2
- data/config/locales/zh-TW.yml +0 -2
- data/decidim-core.gemspec +1 -1
- data/lib/decidim/attachment_attributes.rb +58 -9
- data/lib/decidim/command.rb +1 -1
- data/lib/decidim/core/content_blocks/registry_manager.rb +4 -4
- data/lib/decidim/core/engine.rb +8 -0
- data/lib/decidim/core/test/factories.rb +3 -0
- data/lib/decidim/core/test/shared_examples/admin_resource_gallery_examples.rb +10 -10
- data/lib/decidim/core/test/shared_examples/comments_examples.rb +6 -6
- data/lib/decidim/core/version.rb +1 -1
- data/lib/decidim/map/autocomplete.rb +4 -3
- data/lib/decidim/searchable.rb +5 -0
- data/lib/decidim/view_model.rb +1 -1
- data/lib/tasks/decidim_mailers_tasks.rake +31 -9
- metadata +10 -9
- data/app/commands/decidim/gallery_methods.rb +0 -107
- data/app/packs/src/decidim/vendor/tribute.js +0 -1890
- data/app/packs/stylesheets/decidim/_tribute.scss +0 -36
- data/app/views/decidim/participants_account_mailer/removal_notification.html.erb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50d3683adf392d4b4b2900c05d390051d922759af5aca3a012a0e2d6b1db501a
|
|
4
|
+
data.tar.gz: 6c44f0977cb804af860603ede5ad244515e6cc92d57071cfe594f687ee1c2a96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '097e493989f0f1cdc7d260ba099d36810cb0f85266165a122a39aab7a7befe35560b6efa701c7672541c9016c06cacf455521a9e927fcc1e791bc9caec2d8bcf'
|
|
7
|
+
data.tar.gz: f9df0bd52556860de3d48910823fa559a6a0614eb6aaf7cd335c7e9362659a0ccdcc6be27f84f90d20da6e08f8fb6d6935132bafca33ffaf8d2053f25a015461
|
|
@@ -32,7 +32,7 @@ module Decidim
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
34
|
def render?
|
|
35
|
-
required_keys = [:title, :
|
|
35
|
+
required_keys = [:title, :action_button_title, :action_button_subtitle, :action_button_url]
|
|
36
36
|
required_keys.all? { |key| model.settings.public_send(key).present? }
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -28,7 +28,7 @@ module Decidim
|
|
|
28
28
|
private
|
|
29
29
|
|
|
30
30
|
def cache_hash
|
|
31
|
-
[I18n.locale, highlighted_spaces.map(&:cache_key_with_version)].join(Decidim.cache_key_separator)
|
|
31
|
+
[I18n.locale, model.cache_key_with_version, highlighted_spaces.map(&:cache_key_with_version)].join(Decidim.cache_key_separator)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def section_class
|
|
@@ -15,7 +15,7 @@ module Decidim
|
|
|
15
15
|
def call
|
|
16
16
|
return broadcast(:invalid) unless @form.valid?
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
with_events(with_transaction: true) do
|
|
19
19
|
destroy_user_account!
|
|
20
20
|
destroy_user_identities
|
|
21
21
|
destroy_follows
|
|
@@ -110,5 +110,16 @@ module Decidim
|
|
|
110
110
|
space_manifest.invoke_on_destroy_account(current_user)
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
|
+
|
|
114
|
+
# We use memoization in this particular email, as we want to have the data available before the actual anonymization
|
|
115
|
+
def event_arguments
|
|
116
|
+
@event_arguments ||= {
|
|
117
|
+
user_id: current_user.id,
|
|
118
|
+
user_email: current_user.email,
|
|
119
|
+
user_name: current_user.name,
|
|
120
|
+
locale: current_user.locale,
|
|
121
|
+
organization: current_user.organization
|
|
122
|
+
}
|
|
123
|
+
end
|
|
113
124
|
end
|
|
114
125
|
end
|
|
@@ -5,16 +5,16 @@ module Decidim
|
|
|
5
5
|
private
|
|
6
6
|
|
|
7
7
|
def build_attachments
|
|
8
|
-
@
|
|
9
|
-
@form.
|
|
8
|
+
@attachments = []
|
|
9
|
+
@form.add_attachments.compact_blank.each do |attachment|
|
|
10
10
|
if attachment.is_a?(Hash) && attachment.has_key?(:id)
|
|
11
11
|
update_attachment_title_for(attachment)
|
|
12
12
|
next
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
@
|
|
15
|
+
@attachments << Attachment.new(
|
|
16
16
|
title: title_for(attachment),
|
|
17
|
-
attached_to: @attached_to ||
|
|
17
|
+
attached_to: @attached_to || attachments_attached_to,
|
|
18
18
|
file: signed_id_for(attachment),
|
|
19
19
|
content_type: content_type_for(attachment)
|
|
20
20
|
)
|
|
@@ -26,11 +26,11 @@ module Decidim
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def attachments_invalid?
|
|
29
|
-
@
|
|
30
|
-
next if
|
|
29
|
+
@attachments.each do |attachment|
|
|
30
|
+
next if attachment.valid? || !attachment.errors.has_key?(:file)
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
@form.errors.add(:
|
|
32
|
+
attachment.errors[:file].each do |error|
|
|
33
|
+
@form.errors.add(:add_attachments, error)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
return true
|
|
@@ -41,37 +41,38 @@ module Decidim
|
|
|
41
41
|
|
|
42
42
|
def create_attachments(first_weight: 0)
|
|
43
43
|
weight = first_weight
|
|
44
|
-
# Add the weights first to the old
|
|
45
|
-
|
|
46
|
-
Decidim::Attachment.where(id:
|
|
47
|
-
|
|
44
|
+
# Add the weights first to the old attachments
|
|
45
|
+
attachment_ids = keep_ids
|
|
46
|
+
Decidim::Attachment.where(id: attachment_ids).each do |attachment|
|
|
47
|
+
attachment.update!(weight:)
|
|
48
48
|
weight += 1
|
|
49
49
|
end
|
|
50
|
-
@
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
@attachments.map! do |attachment|
|
|
51
|
+
attachment.weight = weight
|
|
52
|
+
attachment.attached_to = attachments_attached_to
|
|
53
|
+
attachment.save!
|
|
54
54
|
weight += 1
|
|
55
|
-
@form.
|
|
55
|
+
@form.attachments << attachment
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
def
|
|
60
|
-
|
|
59
|
+
def attachment_cleanup!(include_all_attachments: false)
|
|
60
|
+
attachments = include_all_attachments ? attachments_attached_to.attachments.with_attached_file : attachments_attached_to.attachments
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
attachments.each do |attachment|
|
|
63
|
+
attachment.destroy! unless keep_ids.include?(attachment.id)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
attachments_attached_to.reload
|
|
67
|
+
attachments_attached_to.instance_variable_set(:@attachments, nil)
|
|
68
|
+
attachments_attached_to.instance_variable_set(:@photos, nil)
|
|
68
69
|
end
|
|
69
70
|
|
|
70
71
|
def process_attachments?
|
|
71
|
-
@form.
|
|
72
|
+
@form.add_attachments.any?
|
|
72
73
|
end
|
|
73
74
|
|
|
74
|
-
def
|
|
75
|
+
def attachments_attached_to
|
|
75
76
|
return @attached_to if @attached_to.present?
|
|
76
77
|
return form.current_organization if form.respond_to?(:current_organization)
|
|
77
78
|
|
|
@@ -101,8 +102,8 @@ module Decidim
|
|
|
101
102
|
end
|
|
102
103
|
|
|
103
104
|
def keep_ids
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
attachments_array = Array(@form.attachments)
|
|
106
|
+
attachments_array.map do |doc|
|
|
106
107
|
case doc
|
|
107
108
|
when Decidim::Attachment
|
|
108
109
|
doc.id
|
|
@@ -31,13 +31,6 @@ module Decidim
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def process_remove_user(user)
|
|
34
|
-
email = user.email
|
|
35
|
-
name = user.name
|
|
36
|
-
locale = user.locale
|
|
37
|
-
organization = user.organization
|
|
38
|
-
|
|
39
|
-
ParticipantsAccountMailer.removal_notification(email, name, locale, organization).deliver_later
|
|
40
|
-
|
|
41
34
|
Decidim::DestroyAccount.call(
|
|
42
35
|
Decidim::DeleteAccountForm.from_params(
|
|
43
36
|
delete_reason: I18n.t("decidim.account.destroy.inactive_account_removal_reason", inactivity_period: Decidim.delete_inactive_users_after_days)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
class DeleteUserMailer < ApplicationMailer
|
|
5
|
+
# This email is being sent when a user deletes his own account, or when the user was inactive for too long.
|
|
6
|
+
def delete(user_email:, user_name:, locale:, organization:)
|
|
7
|
+
I18n.with_locale(locale) do
|
|
8
|
+
@user_name = user_name
|
|
9
|
+
@organization = organization
|
|
10
|
+
mail(to: user_email, subject: I18n.t("decidim.delete_user_mailer.subject"))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -18,21 +18,5 @@ module Decidim
|
|
|
18
18
|
mail(to: user.email, subject:)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
|
-
|
|
22
|
-
# Notify user about account removal due to inactivity
|
|
23
|
-
def removal_notification(email, name, locale, organization)
|
|
24
|
-
@email = email
|
|
25
|
-
@user_name = name
|
|
26
|
-
@organization = organization
|
|
27
|
-
|
|
28
|
-
I18n.with_locale(locale) do
|
|
29
|
-
subject = I18n.t(
|
|
30
|
-
"decidim.participants_account_mailer.removal_notification.subject",
|
|
31
|
-
organization_name: organization_name(@organization)
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
mail(to: email, subject:)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
21
|
end
|
|
38
22
|
end
|
|
@@ -2,6 +2,14 @@ import { Controller } from "@hotwired/stimulus"
|
|
|
2
2
|
|
|
3
3
|
const OPEN_DELAY_MS = 50
|
|
4
4
|
|
|
5
|
+
const FOCUSABLE_SELECTORS = "a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex='-1'])"
|
|
6
|
+
|
|
7
|
+
const getFocusableElements = (container) => {
|
|
8
|
+
return Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS)).filter(
|
|
9
|
+
(el) => el.offsetParent !== null
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
/**
|
|
6
14
|
* Main menu dropdown controller and traps page scroll while the menu is open.
|
|
7
15
|
*
|
|
@@ -24,6 +32,7 @@ export default class extends Controller {
|
|
|
24
32
|
this.handleButtonClick = this.handleButtonClick.bind(this)
|
|
25
33
|
this.handleKeydown = this.handleKeydown.bind(this)
|
|
26
34
|
this.handleCloseButtonClick = this.handleCloseButtonClick.bind(this)
|
|
35
|
+
this.focusTrapHandler = this.focusTrapHandler.bind(this)
|
|
27
36
|
|
|
28
37
|
this.menuButton.addEventListener("click", this.handleButtonClick)
|
|
29
38
|
this.menuContainer.addEventListener("click", this.handleContainerClick)
|
|
@@ -96,6 +105,23 @@ export default class extends Controller {
|
|
|
96
105
|
return this.menuContainer.getAttribute("aria-hidden") === "true"
|
|
97
106
|
}
|
|
98
107
|
|
|
108
|
+
focusTrapHandler(event) {
|
|
109
|
+
if (event.key !== "Tab") {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const focusable = getFocusableElements(this.menuContainer);
|
|
113
|
+
if (focusable.length === 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (event.shiftKey && document.activeElement === focusable[0]) {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
focusable[focusable.length - 1].focus({ preventScroll: true });
|
|
119
|
+
} else if (!event.shiftKey && document.activeElement === focusable[focusable.length - 1]) {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
focusable[0].focus({ preventScroll: true });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
99
125
|
openMenu() {
|
|
100
126
|
if (typeof this.previousBodyOverflow === "undefined") {
|
|
101
127
|
this.previousBodyOverflow = document.body.style.overflow;
|
|
@@ -104,9 +130,16 @@ export default class extends Controller {
|
|
|
104
130
|
this.element.setAttribute("aria-expanded", "true")
|
|
105
131
|
this.menuContainer.setAttribute("aria-hidden", "false")
|
|
106
132
|
this.menuContainer.setAttribute("aria-modal", "true")
|
|
133
|
+
this.menuContainer.addEventListener("keydown", this.focusTrapHandler)
|
|
134
|
+
|
|
135
|
+
const focusable = getFocusableElements(this.menuContainer);
|
|
136
|
+
if (focusable.length > 0) {
|
|
137
|
+
focusable[0].focus({ preventScroll: true });
|
|
138
|
+
}
|
|
107
139
|
}
|
|
108
140
|
|
|
109
141
|
closeMenu() {
|
|
142
|
+
this.menuContainer.removeEventListener("keydown", this.focusTrapHandler)
|
|
110
143
|
document.body.style.overflow = this.previousBodyOverflow ?? ""
|
|
111
144
|
this.element.setAttribute("aria-expanded", "false")
|
|
112
145
|
this.menuContainer.setAttribute("aria-hidden", "true")
|
|
@@ -20,6 +20,8 @@ describe("MainMenuController", () => {
|
|
|
20
20
|
Menu
|
|
21
21
|
</button>
|
|
22
22
|
<div id="main-menu-container" aria-hidden="true">
|
|
23
|
+
<a href="/link">Link</a>
|
|
24
|
+
<button>Action</button>
|
|
23
25
|
<div id="main-menu-item"></div>
|
|
24
26
|
</div>
|
|
25
27
|
<button id="main-menu-close">Close</button>
|
|
@@ -165,6 +167,81 @@ describe("MainMenuController", () => {
|
|
|
165
167
|
})
|
|
166
168
|
})
|
|
167
169
|
|
|
170
|
+
describe("focusTrapHandler", () => {
|
|
171
|
+
beforeEach(() => {
|
|
172
|
+
jest.spyOn(HTMLElement.prototype, "offsetParent", "get").mockReturnValue(menuContainer)
|
|
173
|
+
controller.openMenu()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
afterEach(() => {
|
|
177
|
+
controller.closeMenu()
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it("focuses the first focusable element when menu opens", () => {
|
|
181
|
+
const focusable = menuContainer.querySelectorAll("a[href], button:not([disabled])")
|
|
182
|
+
const firstEl = focusable[0]
|
|
183
|
+
expect(document.activeElement).toBe(firstEl)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it("cycles focus to first element when Tab is pressed on last focusable element", () => {
|
|
187
|
+
const focusable = menuContainer.querySelectorAll("a[href], button:not([disabled])")
|
|
188
|
+
expect(focusable.length).toBeGreaterThan(1)
|
|
189
|
+
const lastEl = focusable[focusable.length - 1]
|
|
190
|
+
const firstEl = focusable[0]
|
|
191
|
+
|
|
192
|
+
lastEl.focus()
|
|
193
|
+
const event = new KeyboardEvent("keydown", { key: "Tab", bubbles: true })
|
|
194
|
+
jest.spyOn(event, "preventDefault")
|
|
195
|
+
jest.spyOn(firstEl, "focus")
|
|
196
|
+
|
|
197
|
+
menuContainer.dispatchEvent(event)
|
|
198
|
+
|
|
199
|
+
expect(event.preventDefault).toHaveBeenCalled()
|
|
200
|
+
expect(firstEl.focus).toHaveBeenCalledWith({ preventScroll: true })
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it("cycles focus to last element when Shift+Tab is pressed on first focusable element", () => {
|
|
204
|
+
const focusable = menuContainer.querySelectorAll("a[href], button:not([disabled])")
|
|
205
|
+
expect(focusable.length).toBeGreaterThan(1)
|
|
206
|
+
const firstEl = focusable[0]
|
|
207
|
+
const lastEl = focusable[focusable.length - 1]
|
|
208
|
+
|
|
209
|
+
firstEl.focus()
|
|
210
|
+
const event = new KeyboardEvent("keydown", { key: "Tab", shiftKey: true, bubbles: true })
|
|
211
|
+
jest.spyOn(event, "preventDefault")
|
|
212
|
+
jest.spyOn(lastEl, "focus")
|
|
213
|
+
|
|
214
|
+
menuContainer.dispatchEvent(event)
|
|
215
|
+
|
|
216
|
+
expect(event.preventDefault).toHaveBeenCalled()
|
|
217
|
+
expect(lastEl.focus).toHaveBeenCalledWith({ preventScroll: true })
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it("does nothing when a non-Tab key is pressed", () => {
|
|
221
|
+
const event = new KeyboardEvent("keydown", { key: "Enter", bubbles: true })
|
|
222
|
+
jest.spyOn(event, "preventDefault")
|
|
223
|
+
|
|
224
|
+
menuContainer.dispatchEvent(event)
|
|
225
|
+
|
|
226
|
+
expect(event.preventDefault).not.toHaveBeenCalled()
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it("does nothing when there are no focusable elements", () => {
|
|
230
|
+
controller.closeMenu()
|
|
231
|
+
menuContainer.innerHTML = ""
|
|
232
|
+
controller.openMenu()
|
|
233
|
+
|
|
234
|
+
const event = new KeyboardEvent("keydown", { key: "Tab", bubbles: true })
|
|
235
|
+
expect(() => menuContainer.dispatchEvent(event)).not.toThrow()
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it("removes the keydown handler when menu is closed", () => {
|
|
239
|
+
const spy = jest.spyOn(menuContainer, "removeEventListener")
|
|
240
|
+
controller.closeMenu()
|
|
241
|
+
expect(spy).toHaveBeenCalledWith("keydown", controller.focusTrapHandler)
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
168
245
|
describe("disconnect", () => {
|
|
169
246
|
it("removes listeners and closes the menu if open", () => {
|
|
170
247
|
const buttonSpy = jest.spyOn(menuButton, "removeEventListener")
|