Repository navigation
refactor(webapp): one menu for cards and tracks, the media query fallback everywhere, steadier tests - #350
Conversation
ActionMenu (#348) carried its own copy of the panel, its keys and its outside-close listeners beside PopupMenu (#347). It is now a button over PopupMenu, which gains an optional icon, a danger entry, an id, a class, and a placement above its button when there is no room below. The placement is a pure function with its own tests. onClose now says whether focus goes back to the opener: yes for a key or an entry, no for a press elsewhere. Signed-off-by: InstaZDLL <github.105mh@8shield.net>
The reduced-motion hook and the podcasts two-panel hook called addEventListener on a MediaQueryList directly, which throws on a Safari older than 14. Both now go through the fallback #349 added for the theme. Signed-off-by: InstaZDLL <github.105mh@8shield.net>
…missed The podcast test read the route's writes straight after a click, before the route had seen them, so they are now polled. The split test's accessibility scan can outlast the four seconds the confirmation stands on a loaded machine, so it is armed again if it lapsed. Signed-off-by: InstaZDLL <github.105mh@8shield.net>
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Calcul et tests du placement webapp/src/lib/menu-place.ts, webapp/src/lib/menu-place.test.ts |
placeMenu calcule les coordonnées selon l’alignement, l’espace disponible et les limites de la fenêtre. Les tests couvrent ces cas de placement. |
Contrat et comportement de PopupMenu webapp/src/components/popup-menu.tsx |
PopupMenu utilise placeMenu, accepte des options de présentation et transmet un booléen lors de la fermeture pour indiquer si le focus revient à l’élément d’ouverture. |
Intégration de ActionMenu webapp/src/components/action-menu.tsx |
ActionMenu remplace son panneau local par PopupMenu. Lorsqu’une entrée est choisie, le menu rend le focus au bouton avant d’exécuter l’action. |
Abonnements aux requêtes média
| Layer / File(s) | Summary |
|---|---|
Utilisation de onMediaChange webapp/src/components/canvas.tsx, webapp/src/pages/podcasts-page.tsx |
Les deux composants remplacent la gestion directe de l’événement change par onMediaChange. |
Test E2E des podcasts
| Layer / File(s) | Summary |
|---|---|
Attente des écritures du podcast webapp/e2e/smart-playlists-and-podcasts.spec.ts |
Le test attend que podcastWrites contienne les écritures attendues après l’abonnement, le téléchargement et l’actualisation. |
Test E2E de Studio Nocturne
| Layer / File(s) | Summary |
|---|---|
Réarmement de la confirmation Split webapp/e2e/studio-nocturne.spec.ts |
Si le bouton « Split » n’est plus visible avant la confirmation finale, le test clique de nouveau dessus. |
Priority:
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Refactor
Sequence Diagram(s)
sequenceDiagram
participant Bouton
participant ActionMenu
participant PopupMenu
participant placeMenu
Bouton->>ActionMenu: clic
ActionMenu->>PopupMenu: transmet les coordonnées et les entrées
PopupMenu->>placeMenu: demande les coordonnées finales
placeMenu-->>PopupMenu: retourne left et top
PopupMenu-->>ActionMenu: sélection d’une entrée
ActionMenu-->>Bouton: rend le focus puis exécute l’action
Suggested labels: scope: web, scope: components
Merge Risk: 
Minimal · up to e6096
-
No actionable issue remains from this review; the PR is mergeable after normal checks.
Security architecture risk: 
Minimal · up to e6096
-
The shared-menu refactor preserves existing action callbacks and dialog handoffs without introducing new privileges or weakening an identified control. The remaining production changes reuse an existing browser-listener compatibility helper.
Retained concerns
No architecture-level concerns identified.Security review details
-
Security Blast Radius
- inferred — The changed shared contract affects card, track, and artist-image menu interactions within the browser. The traced integration adds no new action callback source or privilege to those consumers.
Trust Boundaries and Controls
- inferred — The changed public interface is a UI component contract, not a new server endpoint. Browser events still select existing caller-defined actions; placement and focus properties do not grant authority or replace authorization enforcement.
-
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
Full details: Description check
-
Explanation
La description est détaillée et couvre le résumé, les changements, les tests et les points d’attention. Elle n’utilise pas les sections exactes du modèle et ne contient pas les cases à cocher ni la confirmation DCO, mais le contenu requis reste majoritairement présent.
-
-
Finishing Touches: 💡 1
-
📝 Generate docstrings: 💡
-
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Warning
Some tools did not complete. Review the errors below.
🔧 Biome (2.5.15)
-
webapp/e2e/smart-playlists-and-podcasts.spec.ts
-
Biome could not lint this file: nested root configuration. Check the repository's Biome configuration and plugins.
webapp/e2e/studio-nocturne.spec.ts
-
Biome could not lint this file: nested root configuration. Check the repository's Biome configuration and plugins.
webapp/src/components/action-menu.tsx
-
Biome could not lint this file: nested root configuration. Check the repository's Biome configuration and plugins.
- 5 others
-
Comment @coderabbitai help to get the list of available commands.
Follow-up to #347, #348 and #349, settling three things they left behind.
One menu instead of two
#347 moved the track menu's panel into
PopupMenu, and #348 wroteActionMenu, which had its own copy of the same panel: the ARIA menu keys, the outside-close listeners and the placement.ActionMenuis now just the "⋯" button. The menu itself isPopupMenu, which gains:icon, so the card menu draws text-only entries;dangerentries, drawn in the danger colour;idforaria-controls, and an extraclassName;above: a menu opened near the bottom of the window hangs upward from its button instead of being pushed up over it. This is whatActionMenualready did.onClose(refocus). It now tells the caller whether focus goes back to the opener: yes for Escape, Tab or a chosen entry, no for a press, a wheel or a gesture elsewhere. The track menu and the artist edit menu ignore the argument and behave exactly as before.Same Tab behaviour everywhere. The Tab handling #348 fixed after review (focus back to the button, then the Tab runs from there) now applies to every menu.
The card menu's entries still close it before they run, so the "Edit" dialog finds focus on the button to hand back. The card menu also picks up the track menu's finer outside-close rules: a long-press drift doesn't close it, and the wheel over a scrollable menu doesn't either.
Placement is a pure function. It now lives in
placeMenu(webapp/src/lib/menu-place.ts), which has its own tests.onMediaChangeeverywhere#349 added a fallback for a
MediaQueryListthat only hasaddListener(Safari before 14), where callingaddEventListenerthrows inside an effect. Two other hooks still calledaddEventListenerdirectly. Both now go through the fallback:canvas.tsx;podcasts-page.tsx, added by feat(webapp): workspace layout, station cards and clearer empty states #348.Steadier tests
main. Since then it passed 16/16 alone, 48/48 run 8 workers wide together with the split test, and in two more full runs with traces kept. Without a trace there was no evidence to fix it on, and CI retries twice.Tests
menu-place.test.ts: the point,align: "end", clamping at every edge, hanging above the button, and staying below when it fits. Disabling the above-rule makes "hangs a menu above its button" fail.main. Typecheck, vitest (323) and build pass.Summary by CodeRabbit