diff --git a/AGENTS.md b/AGENTS.md index d6eea0f54..75fb8ffbd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -306,7 +306,7 @@ Everything in the dashboard must use our own theme, not browser/library defaults - Row interactions: list rows behave like the campaigns list — clicking anywhere on a row opens that item's detail (drawer or page); right-side action buttons (3-dots / "More") either open a relevant detail/tab or drop a short menu of the actions for that row (the mailbox 3-dots menus Settings and Disconnect). A destructive action belongs in that menu as a `danger` item as well as in the detail's own danger zone, because the selection bar is not where anyone looks to remove one row. Inner interactive controls (checkbox, dropdown trigger, action buttons) must `e.stopPropagation()` so they don't also fire the row's open handler. - Prefer realtime over polling: subscribe to the socket and `queryClient.invalidateQueries(...)` on the relevant event instead of `refetchInterval` where an event exists (see `useRealtimeEvents` / `RealtimeManager`). - Interaction details are part of "done". Before calling a dashboard change finished, walk the small things a user hits in the first minute, because these are what make the product feel broken even when the data flow is right: - - every dropdown / popover / picker closes on click-away and on Escape, including when it sits inside a dialog or drawer. Dialog cards stop `mousedown` propagation so the backdrop does not close them; React's `stopPropagation` also stops the native event, so never hand-roll a click-outside listener: every floating layer closes through `useClickOutside` (`@/hooks/useClickOutside`, which `PopoverMenu` uses too). It listens for `pointerdown` in the capture phase, treats a `[data-floating]` layer it opened as inside but the floating panel or dialog holding it as outside, closes on focus moving into an iframe, and takes Escape for the innermost layer only, stopping it there and handing focus back to the trigger. A dialog's own Escape handler still bails out while a `[data-floating]` popover or the `[role="alertdialog"]` confirm is on screen + - every dropdown / popover / picker closes on click-away and on Escape, including when it sits inside a dialog or drawer. Dialog cards stop `mousedown` propagation so the backdrop does not close them; React's `stopPropagation` also stops the native event, so never hand-roll a click-outside listener: every floating layer closes through `useClickOutside` (`@/hooks/useClickOutside`, which `PopoverMenu` uses too). It listens for `pointerdown` in the capture phase, treats a `[data-floating]` layer it opened as inside but the floating panel or dialog holding it as outside, closes on focus moving into an iframe or a tap landing in a same-origin one (a phone moves no focus), and takes Escape for the innermost layer only, stopping it there and handing focus back to the trigger. A dialog's own Escape handler still bails out while a `[data-floating]` popover or the `[role="alertdialog"]` confirm is on screen - toggles are the shared `Toggle` (sky pill, 32x18) from `campaigns/preferences/components/CampaignPreferenceBoolBox`; never hand-roll a switch. If a whole row toggles on click, the switch itself must `stopPropagation` so it does not toggle twice, and a `