ENG-3451: Fix Manage integration modal reporting dirty on mount#7934
ENG-3451: Fix Manage integration modal reporting dirty on mount#7934gilluminate wants to merge 2 commits intomainfrom
Conversation
Replace fragile deep-equality dirty check with Ant Design's form.isFieldsTouched(). The previous logic compared Form.useWatch output to a manually constructed initialValues object, which mismatched on mount (conditionally rendered fields, async-loaded secret schemas, boolean-to-string coercions), causing the Save button to be enabled and the close-confirm prompt to trigger before any user interaction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
There was a problem hiding this comment.
Code Review: ENG-3451 — Fix Manage integration modal dirty state on mount
Summary
This is a clean, minimal, and correct fix. The root cause diagnosis in the PR description is accurate: manually deep-comparing Form.useWatch output against a hand-built initialValues object is fragile because the two representations can differ on mount due to conditional field registration, async secret schema loading, and type coercions — all before the user touches anything.
What the fix does
Replaces the useMemo + isEqual dirty check with form.isFieldsTouched(), which is Ant Design's built-in mechanism for tracking whether a user has actually interacted with any field. This is the idiomatic and semantically correct approach for "has the user changed anything?"
Observations
Reactivity is preserved. Form.useWatch([], form) (kept for the validity useEffect) triggers a re-render on any field value change, so isDirty (now an inline expression) is recomputed at the right times. The onFormStateChange effect's dependency on isDirty will fire correctly when the boolean flips.
useMemo import is correctly retained — it's still used for systemOptions, onSystemSearch, and initialValues elsewhere in the component. Only the now-unused isEqual import is removed.
form.isFieldsTouched() called with no arguments — this uses the default allTouched = false, meaning it returns true when any field has been touched. That's the correct semantics for a "form is dirty" check.
Reset behaviour — Ant Design's form.resetFields() also resets the touched state, so if the form is reset after a successful save, isDirty will correctly return to false.
Minor note
isDirty is now evaluated inline in the render body rather than in a useMemo. This is fine here since form.isFieldsTouched() is a cheap synchronous call and re-renders are already gated by Form.useWatch. No action needed, just worth being aware of if this pattern is adopted in heavier forms.
No issues found
The change is correct, minimal, and well-scoped. No test coverage exists for this component (pre-existing gap, not introduced here). The manual Steps to Confirm in the PR description cover the important variants (DataHub with dataset field, integrations without system field, normal edit/save flow).
🔬 Codegraph: connected (46725 nodes)
💡 Write /code-review in a comment to re-run this review.
Ticket ENG-3451
Description Of Changes
Clicking Manage on an integration connection opened the configure modal with the form already reporting as dirty: the Save button was enabled and cancelling triggered the "unsaved changes" warning even though the user hadn't touched any fields.
Root cause: the form's dirty detection was
!isEqual(Form.useWatch(..), initialValues), comparing a manually constructedinitialValuesobject against the Ant Design form store. The two drift on mount for several reasons — conditionally rendered fields (dataset,system_fides_key) present ininitialValuesbut not registered with the form, async-loaded secret schemas, and value coercions likeString(boolean)— so the deep-equality check returnedfalsebefore the user did anything.Swapped that logic for Ant Design's built-in
form.isFieldsTouched(), which only flips totrueonce the user has actually interacted with a field.Form.useWatchis retained to drive re-renders for the existing validity check.Code Changes
clients/admin-ui/src/features/integrations/add-integration/ConfigureIntegrationForm.tsx— replaced theuseMemo+isEqualdirty check withform.isFieldsTouched(); dropped the now-unusedisEqualimport.Steps to Confirm
npm run devinclients/admin-ui/) with a local backend and log in.Pre-Merge Checklist
CHANGELOG.mdupdated