Fix translating taxonomies items doesn't work#16122
Fix translating taxonomies items doesn't work#16122andreslucena merged 13 commits intodecidim:developfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces the old initLanguageChangeSelect initializer with a Stimulus Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Select as "select.language-change"
participant Controller as "Stimulus language-change"
participant Tabs as "Tabs container (tab-content)"
User->>Select: choose language option
Select->>Controller: change event
Controller->>Tabs: find current active pane
Controller->>Tabs: set current pane aria-hidden=true, remove is-active
Controller->>Tabs: activate target pane aria-hidden=false, add is-active
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@decidim-admin/app/packs/entrypoints/decidim_admin.js`:
- Around line 29-30: The forEach callback currently uses an implicit-return
arrow which triggers the lint rule; change the callback to use an explicit block
body that calls window.deprecate without returning its value. Locate the query
selector expression
document.querySelectorAll("select.language-change").forEach(...) and replace the
arrow callback with a block-style callback that invokes
window.deprecate(container, "language-change", "select.language-change") inside
the block (no return), ensuring the lint error is resolved.
In
`@decidim-admin/app/packs/src/decidim/admin/controllers/language-change/controller.js`:
- Around line 13-21: In handleChange, the DOM traversal
(event.target.parentElement.parentElement.nextElementSibling) and unchecked
querySelector(".is-active") calls can return null and cause runtime errors; fix
by locating the tabs container via a stable selector (e.g., a data attribute or
a Stimulus target) instead of fragile parent/next traversal, then guard all DOM
lookups with null checks (ensure tabsContent exists, assign result of
tabsContent.querySelector(".is-active") to a variable and check it before
accessing ariaHidden/classList, and likewise check the targetTabPane element
before toggling). Update the handler to use these safer lookups and
short-circuit if required elements are missing.
In `@decidim-forms/app/packs/src/decidim/forms/admin/forms.js`:
- Around line 392-393: The forEach callback on
document.querySelectorAll("select.language-change") currently uses a concise
arrow that implicitly returns window.deprecate(...); change the callback to a
block body (wrap in { ... }) and call window.deprecate(container,
"language-change", "select.language-change") inside the braces (no returned
value) so the forEach callback does not implicitly return; this affects the
forEach iterator where window.deprecate is invoked for each container.
🧹 Nitpick comments (3)
decidim-admin/app/packs/src/decidim/admin/controllers/language-change/controller.js (1)
3-11: Consider using Stimulus conventions instead of manual event listener management.Stimulus provides a declarative
data-actionattribute for binding events and lifecycle-managed method binding. ManualaddEventListener/removeEventListener+.bind(this)is unnecessary boilerplate when the framework handles it. For example, the form builder could emitdata-action="change->language-change#handleChange"on the<select>, and you could remove theconnect/disconnecthooks entirely.decidim-admin/spec/system/admin_manages_taxonomies_spec.rb (2)
192-192: Avoidsleep(2)in system specs — use Capybara's waiting assertions instead.Hard sleeps make tests slow and flaky. Capybara's matchers already retry/wait. Consider waiting on a visible UI change (e.g., a success flash message or the updated content) before asserting on the database:
expect(page).to have_content("My english value") # wait for UI update expect(taxonomy.reload.children.last.name["en"]).to eq("My english value")The same applies to line 206.
143-145: Redundantlet(:organization)— identical to the outer definition on line 6.Line 144 redefines
organizationwith the same factory call as line 6. If no customization is intended, this can be removed.
decidim-admin/app/packs/src/decidim/admin/controllers/language-change/controller.js
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Fixes multilingual taxonomy item editing by ensuring the language selector properly switches the active translation pane (especially for dynamically loaded/drawer content), preventing secondary-language edits from overwriting the primary language.
Changes:
- Replaces the legacy
select.language-changeevent binding with a Stimuluslanguage-changecontroller. - Updates the shared
FormBuilderlanguage selector markup to emitdata-controller="language-change". - Adds a system spec covering independent persistence of translations when an organization has >4 locales.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
decidim-forms/app/packs/src/decidim/forms/admin/forms.js |
Removes legacy initializer call; adds deprecation checks after dynamic field insertion. |
decidim-core/lib/decidim/form_builder.rb |
Adds data-controller="language-change" to the language selector <select>. |
decidim-admin/spec/system/admin_manages_taxonomies_spec.rb |
Adds coverage for creating/updating taxonomy item translations in multiple locales. |
decidim-admin/app/views/decidim/admin/taxonomies/_taxonomy_actions.html.erb |
Fixes aria-label to use translated_attribute for taxonomy names. |
decidim-admin/app/packs/src/decidim/admin/controllers/language-change/controller.js |
New Stimulus controller that toggles the active translation pane on <select> change. |
decidim-admin/app/packs/src/decidim/admin/choose_language.js / decidim-admin/app/packs/entrypoints/decidim_admin.js |
Removes old JS and replaces initialization with deprecation warnings. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
🧹 Nitpick comments (3)
decidim-admin/spec/system/admin_manages_taxonomies_spec.rb (3)
157-164: Hardcoded locale reset inaftermay drift from actual defaults.The
afterblock resets locales to%w(en ca es), but if the test suite's default locales ever change, this will silently diverge. Consider capturing the original values inbeforeand restoring them inafter.♻️ Suggested approach
+ let(:original_i18n_locales) { I18n.available_locales } + let(:original_decidim_locales) { Decidim.available_locales } + before do + # originals are captured by the lets above before mutation I18n.available_locales = available_locales ... end after do - I18n.available_locales = %w(en ca es) - Decidim.available_locales = %w(en ca es) + I18n.available_locales = original_i18n_locales + Decidim.available_locales = original_decidim_locales I18n.backend.reload! ... end
191-193: Replacesleep(2)with Capybara waiting assertions.
sleepis a common source of flaky tests in CI — too short on slow runners, too long everywhere else. Since the create/update actions likely produce a flash message or redirect, wait on a visible indicator instead, then assert on the DB.- sleep(2) + expect(page).to have_content("Item successfully created") # or whatever flash text is shown expect(taxonomy.reload.children.last.name["en"]).to eq("My english value")- sleep(2) + expect(page).to have_content("Item successfully updated") # or whatever flash text is shown expect(taxonomy.reload.children.last.name["en"]).to eq("My modified english value")Also applies to: 205-207
174-182:first('input[type="text"]', visible: true)could be fragile if tab switch hasn't completed.Consider adding a brief assertion that the expected tab panel is active before interacting with the input, or use a more specific selector targeting the locale panel (e.g.,
find('[id*="panel"]', visible: true) input). This guards against race conditions where the previous panel's input is still visible.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
decidim-admin/spec/system/admin_manages_taxonomies_spec.rb (2)
146-164:remove_const+loadis fragile; consider extracting a shared helper.The
send(:remove_const, :TaxonomyForm)/loaddance appears both in thebeforeandafterblocks. If more specs need multi-locale form testing, this boilerplate will be duplicated. A small shared helper (e.g.,reload_form_class(Decidim::Admin, :TaxonomyForm)) would reduce duplication and keep the teardown in sync with setup.Also, the
afterblock hardcodes%w(en ca es)— if the default test locales ever change, this will silently produce the wrong reset. Consider capturing the original values before overriding:Sketch
# inside the context block let(:original_i18n_locales) { I18n.available_locales } let(:original_decidim_locales) { Decidim.available_locales } after do I18n.available_locales = original_i18n_locales Decidim.available_locales = original_decidim_locales # ... end
174-182:first('input[type="text"]', visible: true)is fragile — scope to the active tab panel.If the form ever gains another visible text input (e.g., a code or slug field), this selector will silently fill the wrong field without failing. A safer approach is to scope within the active tab panel:
Example
- first('input[type="text"]', visible: true).set("My english value") + find(".tabs-content [aria-hidden='false'] input[type='text']").set("My english value")(Adjust the panel selector to match the actual DOM structure produced by
translated/ the Stimulus controller.)The same applies to lines 179, 197, and 200.
013b2ec
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
decidim-admin/spec/system/admin_manages_taxonomies_spec.rb (1)
6-22:⚠️ Potential issue | 🔴 Critical
available_localesis undefined for most test contexts — all tests outside the nested context will error.
available_localesis referenced in the top-levelbeforeblock (lines 11, 12, 18) but is only defined vialetat line 162 insidecontext "and the organization has multiple languages". Every other test context will raise aNameError(or RSpec's "method undefined" equivalent) when thebeforeblock runs.Add a top-level
letwith the default locales (matching the hardcoded restore inafter):Proposed fix
let(:organization) { create(:organization) } let(:user) { create(:user, :admin, :confirmed, organization:) } let(:attributes) { attributes_for(:taxonomy) } + let(:available_locales) { %w(en ca es) } before do I18n.available_locales = available_locales
🧹 Nitpick comments (2)
decidim-admin/spec/system/admin_manages_taxonomies_spec.rb (2)
25-32: Consider saving and restoring original locale values instead of hardcoding.The
afterblock hardcodes%w(en ca es)as the restored defaults. If the project's default test locales ever change, this will silently drift. Capturing the originals before mutation is more resilient:Sketch
# at the top of the describe block: let(:original_i18n_locales) { I18n.available_locales } let(:original_decidim_locales) { Decidim.available_locales } after do I18n.available_locales = original_i18n_locales Decidim.available_locales = original_decidim_locales # ... endNote:
letis lazily evaluated, so these would be captured on first access — you'd need to force evaluation before thebeforeblock mutates them (e.g., withlet!or by referencing them inbeforebefore the mutation).
172-180: Thefirst('input[type="text"]', visible: true)selector is fragile.If the form ever gains additional visible text inputs (e.g., a slug or code field), this will silently target the wrong element. Consider scoping to the active tab panel or using a more specific selector tied to the locale field name, e.g.:
find('input[name*="[name_en]"]') # or scope within the visible panel: within(".tabs-panel.is-active") { fill_in ... }This is fine for now given the current form structure.
dc5ad27
013b2ec to
dc5ad27
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
decidim-admin/spec/system/admin_manages_taxonomies_spec.rb (1)
145-166: Restore original locales instead of hard-coding defaults.Hard-coding
%w(en ca es)in the teardown couples this spec to a specific global default and can leak into later specs if defaults change. Capture and restore the previous values for better test isolation.♻️ Suggested change
before do + `@original_i18n_locales` = I18n.available_locales + `@original_decidim_locales` = Decidim.available_locales I18n.available_locales = %w(en ca es fr it) Decidim.available_locales = %w(en ca es fr it) I18n.backend.reload! @@ after do - I18n.available_locales = %w(en ca es) - Decidim.available_locales = %w(en ca es) + I18n.available_locales = `@original_i18n_locales` + Decidim.available_locales = `@original_decidim_locales` I18n.backend.reload!🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@decidim-admin/spec/system/admin_manages_taxonomies_spec.rb` around lines 145 - 166, Store the current I18n.available_locales and Decidim.available_locales into local variables at the start of the spec (before you change them), then in the after block restore those saved values and call I18n.backend.reload!; update the teardown to use the saved variables instead of the hard-coded %w(en ca es) so I18n.available_locales and Decidim.available_locales are returned to their original states (keep the existing remove_const/load and organization.update! handling unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@decidim-admin/spec/system/admin_manages_taxonomies_spec.rb`:
- Around line 145-166: Store the current I18n.available_locales and
Decidim.available_locales into local variables at the start of the spec (before
you change them), then in the after block restore those saved values and call
I18n.backend.reload!; update the teardown to use the saved variables instead of
the hard-coded %w(en ca es) so I18n.available_locales and
Decidim.available_locales are returned to their original states (keep the
existing remove_const/load and organization.update! handling unchanged).
|
Tested and working locally, thanks <3 |
andreslucena
left a comment
There was a problem hiding this comment.
I still didn't try it out locally, but checking out the code it looks good. Can you add this comment please? Thanks
decidim-core/app/packs/src/decidim/controllers/language_change/controller.js
Show resolved
Hide resolved
|
Add |

🎩 What? Why?
In this PR i am fixing the taxonomies that cannot be translated. While investigating, i have found that the language selector was not registering the tab change event, therefore everytime you'd select a new language, only default was being changed.
📌 Related Issues
Link your PR to an issue
Testing
📷 Screenshots
Please add screenshots of the changes you are proposing

Summary by CodeRabbit
Refactor
UX / Accessibility
Tests