✨ Add ImpersonateDropdown navigation component#25
Conversation
Closes #15. A navbar dropdown that lets ROLE_ALLOWED_TO_SWITCH users start impersonating someone else. A debounced search input queries a project-side JSON endpoint and renders the results as clickable rows that navigate to ?_switch_user=<email>. Ships a Stimulus controller (assets/dist/impersonate_dropdown.js, registered lazy in assets/package.json so the JS only loads when the dropdown is actually present on the page). Results are rendered with textContent, in-flight requests are aborted when the query changes, and no fetch happens under 2 characters. Pairs with #16 ImpersonateBanner — the dropdown lets the user enter impersonation, the banner signals it is active.
|
Smoke-tested in Impala — rendered the dropdown inside the Enabel navbar, wired a stub What I verified
One tiny doc nit (optional) The LGTM, ready to tag. |
The Stimulus controller already supported a minLength value (default 2) but it was hardcoded on the JS side. Lift it to the PHP component so projects can override it from the call site, consistent with debounce and exitParameter.
|
Addressed in 8eaf408 — Tests bumped to 246/246 (one extra assertion in the defaults/custom tests + one new invalid-type test). |
Summary
Adds an
ImpersonateDropdownnavigation component that letsROLE_ALLOWED_TO_SWITCHusers start impersonating someone else through a debounced JSON-search dropdown in the navbar.<li class="nav-item dropdown">with an icon toggle, a search input, and a result list — drop it insideEnabel:Ux:Menulike any other navbar itemassets/dist/impersonate_dropdown.js) registeredlazyinassets/package.json, so the JS is only fetched when an element withdata-controlleris on the page (i.e. for users who actually have the role)Enabel:Ux:ImpersonateBanner(which signals an active impersonation session)Endpoint contract (consumer-side)
The component issues
GET {searchUrl}?q={query}after a 250 ms debounce, expecting:[ { "email": "jane.doe@enabel.be", "displayName": "Jane Doe", "initials": "JD" } ]Click navigates to the current URL with
?_switch_user=<email>appended — the contract Symfony's Security component listens for.XSS safety
All rendered fields go through
textContent, neverinnerHTML. The click-target URL is built vianew URL()+URLSearchParams.set, not string concatenation. A hostile JSON payload from the search endpoint cannot inject markup or scripts.API
searchUrlstringsearchPlaceholderstring''noResultsLabelstring'No results'iconstring'fa6-solid:user-secret'title?stringnullexitParameterstring'_switch_user'debounceint250exitParameteranddebounceare not in the original issue spec — added so projects with customfirewall.context.switch_user.parameterconfigs or slower backends don't have to override the template.Test plan
searchUrl, defaults, custom params, type validation)data-controlleris not present for users withoutROLE_ALLOWED_TO_SWITCHCloses #15.