Skip to content

ENG-3180: Migrate messaging template forms to Ant Design#7939

Open
gilluminate wants to merge 3 commits intomainfrom
gill/ENG-3180/messaging-templates-antd
Open

ENG-3180: Migrate messaging template forms to Ant Design#7939
gilluminate wants to merge 3 commits intomainfrom
gill/ENG-3180/messaging-templates-antd

Conversation

@gilluminate
Copy link
Copy Markdown
Contributor

@gilluminate gilluminate commented Apr 15, 2026

Ticket ENG-3180

Description Of Changes

Part of the ENG-3165 epic to eliminate Chakra form components across admin-ui. The messaging template forms in clients/admin-ui/src/features/messaging-templates/ previously used Formik + Chakra-backed wrappers (CustomTextInput, CustomTextArea, CustomSwitch). They now use antd Form + Form.Item + native antd inputs, matching the pattern established by OrganizationForm, SharedMonitorConfigForm, and DeleteUserModal.

API contracts and submit payload shapes are unchanged so the backend is unaffected and existing Cypress coverage stays valid. The Save button derives its disabled state directly from form.isFieldsTouched() + form.getFieldsError() inside a shouldUpdate render-prop — no extra useState/useEffect for dirty tracking. The form re-mounts via a content-hashed key prop so antd's "touched" state resets after a successful save without a setFieldsValue dance.

AddMessagingTemplateModal was also tidied up: it now uses the standard modal body pattern (footer={null} with action buttons inside the body), Typography.Paragraph instead of bespoke Tailwind, and the template-type-selector test id sits directly on the <Select>.

Code Changes

  • PropertySpecificMessagingTemplateForm.tsx — Formik → antd Form. Replaced CustomTextInput/CustomTextArea/CustomSwitch with Input/Input.TextArea/Switch. Wrapped ScrollableList in a controlled PropertiesList adapter that bridges antd's value/onChange to ScrollableList's values/setValues. Replaced FormSection with antd Card. Submit-button disabled state derives from form built-ins inside a shouldUpdate render-prop. Form re-mounts via a content-hash key to reset touched state after save.
  • EmailTemplatesForm.tsx — Formik → antd Form with nested name arrays. form.resetFields() + form.setFieldsValue(values) re-baselines after a successful save. Same shouldUpdate submit pattern. FormSection replaced with antd Card.
  • AddMessagingTemplateModal.tsx — dropped ChakraBox/ChakraText aliases. Moved action buttons out of the footer prop into the standard body Flex. Switched description copy to Typography.Paragraph. Removed the wrapper <div> around Select and moved data-testid="template-type-selector" directly onto the Select. Fixed a pre-existing invalid gap="medium"gap="small" on antd Flex.
  • pages/notifications/templates/add-template.tsx and [id].tsx — destructure isLoading off the create/put mutation and pass as isSaving prop to the form (the form no longer owns the submitting state).
  • cypress/e2e/messaging.cy.ts — updated the template-type-selector assertion to call .antSelect(...) directly on the testid'd Select element instead of .find(".ant-select").antSelect(...).

Steps to Confirm

  1. cd clients/admin-ui && npm run dev, log in with root_user / Testpassword1!
  2. Settings → Email templates: edit a subject and body, confirm Save enables only after a change, save succeeds and shows a toast.
  3. Notifications → Templates → New: open the modal, pick a template type from the Select, confirm Next enables and routes to the add page.
  4. Add a property-specific template: fill subject/body, toggle Enable, attach properties, save — confirm redirect + success toast.
  5. Edit an existing property-specific template: confirm initial values populate, Save is disabled until you edit, Delete still opens the confirmation modal.
  6. Validation: clear required fields and submit — verify inline rules errors display under each field.
  7. Run npx cypress run --spec cypress/e2e/messaging.cy.ts from clients/admin-ui — should pass.

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • All UX related changes have been reviewed by a designer
    • No UX review needed
  • Followup issues:
    • Followup issues created
    • No followup issues
  • Database migrations:
    • Ensure that your downrev is up to date with the latest revision on main
    • Ensure that your downgrade() migration is correct and works
      • If a downgrade migration is not possible for this change, please call this out in the PR description!
    • No migrations
  • Documentation:
    • Documentation complete, PR opened in fidesdocs
    • Documentation issue created in fidesdocs
    • If there are any new client scopes created as part of the pull request, remember to update public-facing documentation that references our scope registry
    • No documentation updates required

gilluminate and others added 2 commits April 15, 2026 15:26
Replace Formik + Chakra-backed inputs (CustomTextInput, CustomTextArea,
CustomSwitch) with antd Form + Form.Item + native antd inputs across
the messaging-templates feature. Submit button derives disabled state
from form.isFieldsTouched() + form.getFieldsError() inside a
shouldUpdate render-prop, with no extra useState/useEffect. The form
re-mounts via a content-hashed key prop so antd's "touched" state
resets after a successful save without a setFieldsValue dance.

AddMessagingTemplateModal switches to the standard modal body pattern
(footer={null} with buttons inside the body) and Typography.Paragraph,
and the template-type-selector test id moves directly onto the Select.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fides-plus-nightly Ready Ready Preview, Comment Apr 15, 2026 10:00pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
fides-privacy-center Ignored Ignored Apr 15, 2026 10:00pm

Request Review

@gilluminate gilluminate marked this pull request as ready for review April 15, 2026 21:28
@gilluminate gilluminate requested a review from a team as a code owner April 15, 2026 21:28
@gilluminate gilluminate requested review from Kelsey-Ethyca and speaker-ender and removed request for a team and speaker-ender April 15, 2026 21:28
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: Migrate messaging template forms to Ant Design

Overall, this is a well-structured migration. The shouldUpdate render-prop for disabled Save state, the controlled PropertiesList adapter (value/onChange), and the use of Form.Item with nested field names are all the right antd patterns. The removal of Chakra and Formik dependencies is clean.

Must Fix

1. router.push() in Cancel button (PropertySpecificMessagingTemplateForm.tsx:77)
The Cancel button uses router.push() in an onClick handler. This should be a <NextLink href={...} passHref> wrapping the <Button> instead, which gives correct <a> semantics, right-click / open-in-new-tab support, and Next.js prefetching. The handleCancel function and useRouter import can then be dropped.

2. isFieldsTouched() may not detect property-only edits (PropertySpecificMessagingTemplateForm.tsx:158)
PropertiesList drives the properties field via onChange?.(newValues) through ScrollableList. If antd doesn't mark that field as "touched" through the Form.Item value-control bridge, a user who only adds/removes a property (without touching subject or body) will find Save permanently disabled. This is a potential user-facing bug. Fix by explicitly calling form.setFields([{ name: 'properties', touched: true }]) in PropertiesList's onChange, or by using a useState dirty flag.

Suggestions

3. formKey includes mutable template content (PropertySpecificMessagingTemplateForm.tsx:91)
Including content.subject and content.body in the form's key means any background re-fetch returning data that differs from the user's in-progress edits will silently discard those edits (a React key change causes a full unmount). Scope the key to template.id (or template.id + template.type) only, and use form.setFieldsValue in the parent page's save-success callback instead.

4. resetFields() + setFieldsValue() ordering (EmailTemplatesForm.tsx:56-57)
The sequence is correct, but the dependency between the two calls is non-obvious to the next reader. A comment explaining the intent (or collapsing to just setFieldsValue(values)) would improve maintainability.

5. destroyOnHidden prop name (AddMessagingTemplateModal.tsx:37)
Verify that the fidesui Modal wrapper forwards destroyOnHidden. In some antd versions this prop is named destroyOnClose. If it isn't forwarded, the selectedTemplateId state won't reset between modal openings.


🔬 Codegraph: connected (46727 nodes)


💡 Write /code-review in a comment to re-run this review.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 15, 2026

Title Lines Statements Branches Functions
admin-ui Coverage: 8%
6.1% (2663/43645) 5.22% (1293/24747) 4.19% (542/12916)
fides-js Coverage: 78%
78.98% (1962/2484) 65.55% (1214/1852) 72.57% (336/463)
privacy-center Coverage: 88%
85.97% (331/385) 81.36% (179/220) 78.87% (56/71)

@gilluminate
Copy link
Copy Markdown
Contributor Author

Re: review item #5 (destroyOnHidden on AddMessagingTemplateModal) — fidesui's Modal is a direct re-export of antd's Modal, and we're on antd v6 where destroyOnHidden is the current prop name (it replaced the deprecated destroyOnClose). So this one's already correct.

- Swap Cancel Button for NextLink wrapper for proper link semantics
- Scope formKey to template.id || template.type so background refetches
  don't discard in-progress edits
- Reset touched state via onFinish wrapper on successful save; handleSubmit
  now returns Promise<boolean> so the form can tell success from failure
- Expand inline comment explaining why EmailTemplatesForm needs both
  resetFields() and setFieldsValue() after a successful save
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant