fix(plugin-form): guard modal/drawer against accidental discard of unsaved input - #2028
Merged
Conversation
…saved input Create/edit forms shown as a modal or right-side drawer let users wipe their input by closing mid-entry — a backdrop click, Escape, or the X silently dropped everything they'd typed. Now an *accidental* close (backdrop / Escape / X) on a dirty form first asks "Discard changes?" (Keep editing / Discard), so input is never lost without confirmation. The explicit Cancel button is treated as an intentional discard and still closes immediately — no redundant prompt. Opt out per-form with `confirmOnDiscard: false`. Details: - Dirty state is computed in the form renderer via a normalized comparison (empty-ish values treated as equal) surfaced through a new `onDirtyChange` callback, instead of react-hook-form's identity-based `isDirty`, which false-positived on fields that self-normalize their empty value on mount (a pristine create form would otherwise prompt). - Both overlays render their action buttons in a sticky footer and route Cancel through the guard directly; the renderer's built-in Cancel did a `form.reset()` before onCancel, which emptied the form before the prompt could appear — so "Keep editing" kept an already-wiped form. - The discard AlertDialog is nested inside the Dialog/Sheet content so Radix stacks the layers correctly and its buttons receive clicks. - Discard-guard strings localized (en/zh) via createSafeTranslation. Adds discardGuard.test.tsx covering both overlays: Cancel closes directly even when dirty; accidental close prompts; Keep editing preserves input; Discard closes; confirmOnDiscard:false closes immediately.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
PageView.test.tsx transitively imports ObjectForm -> Drawer/ModalForm, which now build their discard-guard strings via createSafeTranslation. The test's full i18n module mock didn't export it, so the suite failed to load. Add a faithful stub that echoes the supplied English defaults.
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
baozhoutao
added a commit
that referenced
this pull request
Jul 30, 2026
…aved input (#2998) The in-app discard guard (#2028) only intercepts Radix close paths — backdrop, Escape, the X. A browser refresh or tab close never reaches them: beforeunload fired with nobody calling preventDefault, so a dirty create/edit overlay silently lost everything the user had typed (the dialog itself is even restored afterwards via the ?recordId= URL param, but empty). Register a beforeunload listener while the overlay is open and dirty (and confirmOnDiscard hasn't been opted out), mirroring the existing StudioDesignSurface pillar guard. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Create/edit forms shown as a modal or right-side drawer let users wipe their input by closing mid-entry — a backdrop click, Escape, or the X silently dropped everything they'd typed. (用户反馈:弹窗/抽屉表单很容易误触关闭,导致已输入的数据丢失。)
Behavior now
confirmOnDiscard: false.How it works
onDirtyChangecallback — instead of react-hook-form's identity-basedisDirty, which false-positived on fields that self-normalize their empty value on mount (a pristine create form would otherwise prompt on close).form.reset()beforeonCancel, which emptied the form before the prompt could appear — so "Keep editing" kept an already-wiped form.AlertDialogis nested inside the Dialog/Sheet content so Radix stacks the layers correctly and its buttons receive clicks (as a sibling, the still-open overlay swallowed them and "Keep editing" did nothing).createSafeTranslation.Tests
Adds
discardGuard.test.tsxcovering both overlays:confirmOnDiscard: falsecloses immediatelyAll 148 plugin-form tests pass. Manually verified in a browser repro (modal + drawer): Cancel closes directly, Escape/X prompt when dirty, Keep editing preserves the typed value.