🥂 feat: Toasts on All Save Actions#74
Conversation
Three copies of @radix-ui/react-dismissable-layer were getting installed (1.1.0 nested under react-tooltip, 1.1.1 hoisted via react-menu, 1.1.11 nested under the react-dialog 1.1.15 override). Each module copy holds its own DismissableLayerContext and originalBodyPointerEvents module variable, so when the sidebar Dropdown opens, the user clicks Settings, and the SettingsDialog mounts before the Dropdown portal has finished unmounting, the Dialog's copy snapshots document.body with the Dropdown's "none" still applied. When the Dialog later closes, its cleanup writes that stale "none" back onto the body and leaves the whole app unclickable until reload. Pin a single shared version (1.1.11, the newest already pulled in by react-dialog 1.1.15) so all Radix packages route through one module copy with one shared layer context.
|
@codex review |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@dustinhealy can you try installing the package explicitly instead of adding an override? |
* feat: success/error toasts on every admin panel save action Wires click-ui's Toast component into every save mutation in the admin panel: config editor (per-field, bulk, YAML import), groups (create / edit / delete), roles (create / edit / delete), users (invite, delete, role / group assignment, user profile create / delete), system grants (EditCapabilitiesDialog), and per-field profile mutations (useProfileMutations). Uses click-ui's official createToast directly; ClickUIProvider already mounts the matching ToastProvider, so the global createToast routes through it. Toasts inherit click-ui brand colours, WCAG contrast, keyboard accessibility, swipe-to-dismiss, close button, ARIA live region, and 5s auto-dismiss from the library. src/utils/toast.ts is a five-line wrapper exposing notifySuccess and notifyError that delegate to click-ui's createToast. Call sites pass the affected resource through mutation variables (not closed-over component state) so the toast renders the correct name even when the confirm dialog has already cleared its target by the time the response lands. Refs AI-1206. * fix: throw instead of silently no-op when target missing in edit mutations EditGroupDialog, EditRoleDialog and EditCapabilitiesDialog mutationFns used to bail with an empty return when their target (group, role, principal) was missing, which React Query treats as success and which caused the new onSuccess handlers to fire a success toast and close the dialog without anything having been persisted. Throw a localised error in the unavailable case so onError fires instead, and add a matching guard at each call site so mutate is never invoked with a missing target in the first place. * fix: capture submitted name in mutation variables for create/edit toasts The five create and edit dialogs used to read the resource name from component state inside onSuccess instead of from the value that was submitted with the mutation. If the user edited the name field while the request was in flight (or the dialog reset before the toast fired), the toast could render an empty or wrong name even though the server saved the original value. Pass the submitted name through the mutation variables, use it for the actual API call, and read it back from the mutation's data or variables argument in onSuccess so the toast always reflects what was persisted.
Tried it, wasn't able to click on anything in admin panel after closing modal. Without the override, the exact version pins all end up keeping their nested copies and so the bug persists |
|
Closing as superseded. The dedupe and the toast work got entangled on this branch when #75 squash-merged into the dedupe branch instead of into Reopened as two parallel PRs against
|
Summary
Three copies of @radix-ui/react-dismissable-layer were getting installed (1.1.0 nested under react-tooltip, 1.1.1 hoisted via react-menu, 1.1.11 nested under the react-dialog 1.1.15 override). Each module copy holds its own DismissableLayerContext and originalBodyPointerEvents module variable, so when the sidebar Dropdown opens, the user clicks Settings, and the SettingsDialog mounts before the Dropdown portal has finished unmounting, the Dialog's copy snapshots document.body with the Dropdown's "none" still applied. When the Dialog later closes, its cleanup writes that stale "none" back onto the body and leaves the whole app unclickable until reload.
Pin a single shared version (1.1.11, the newest already pulled in by react-dialog 1.1.15) so all Radix packages route through one module copy with one shared layer context.
Change Type
Testing
Confirmed behavior no longer present after using theme switcher, app buttons are now interactable again when closing settings dialog modal.
Checklist
Note
Low Risk
Dependency resolution-only change with no application code edits; risk is limited to Radix overlay/dismiss behavior across the pinned version.
Overview
Adds a
package.json/bun.lockoverride pinning@radix-ui/react-dismissable-layerto1.1.11, alongside the existing@radix-ui/react-dialogpin, so every Radix consumer resolves one copy of the dismissable-layer module.That deduplication fixes a race where opening Settings from a sidebar dropdown while the dropdown portal is still tearing down left
document.bodywithpointer-events: noneafter the settings dialog closed, because separate module copies each maintained their own body snapshot and cleanup.Reviewed by Cursor Bugbot for commit 2a2c777. Bugbot is set up for automated code reviews on this repo. Configure here.