Skip to content

feat(form-builder): free field reordering via field_order#8125

Open
Kelsey-Ethyca wants to merge 4 commits intodemo/aprilfrom
feat/pc-form-builder-field-order
Open

feat(form-builder): free field reordering via field_order#8125
Kelsey-Ethyca wants to merge 4 commits intodemo/aprilfrom
feat/pc-form-builder-field-order

Conversation

@Kelsey-Ethyca
Copy link
Copy Markdown
Contributor

@Kelsey-Ethyca Kelsey-Ethyca commented May 6, 2026

Ticket ENG-3652

Description Of Changes

Lets the privacy center form builder freely reorder identity and custom fields — including interleaving (e.g. a "reason" input between Email and Phone). The legacy persisted shape splits an action's fields into two top-level buckets (identity_inputs and custom_privacy_request_fields), so the public renderer hardcoded a fixed sequence (name → email → phone → customs) and the form builder's drag-and-drop order was lost on save.

This adds a single optional field_order: string[] on PrivacyRequestOption. When present, the privacy center renders strictly in that order, looking each key up in either bucket. When absent, the legacy hardcoded sequence still applies — every existing customer config keeps working with no migration.

custom_privacy_request_field_order is now deprecated. It's still read as a fallback for legacy configs, but new writes from the form builder use field_order. The fidesplus storage normalizer (separate PR) skips writing the legacy key when field_order is already present.

Pairs with a fidesplus PR for the storage-side normalization update + integration test.

Code Changes

  • field_order: Optional[List[str]] added to PrivacyRequestOption with a Pydantic validator (rejects duplicates and unknown keys; allows partial coverage with a documented fall-through to legacy ordering)
  • reorder_custom_privacy_request_fields updated to prefer field_order over the deprecated custom_privacy_request_field_order
  • Form builder mapper now emits fieldOrder in MapResult, walking the spec's children list
  • Form builder synthesize accepts an optional fieldOrder argument and renders elements in that sequence (falls back to the canonical name → email → phone → customs order otherwise)
  • FormBuilderPage and the [actionPolicyKey].tsx save route plumb fieldOrder through; on save we explicitly drop the deprecated custom_privacy_request_field_order key so it can't shadow the new source of truth
  • Public renderer (PrivacyRequestForm) refactored to a single ordered loop driven by OrderedField[] from a new pure helper (buildOrderedFields); per-field validation, hidden filtering, isFieldVisible all preserved
  • New unit tests for mapSpecToPcShape (3 cases), synthesizeSpecFromPcShape (5 cases), buildOrderedFields (9 cases), Pydantic validator (7 cases), and the reorder helper (2 cases for the deprecation precedence)
  • Cypress e2e test asserting field_order is sent in the property PUT body when a custom field sits between identity fields

Steps to Confirm

  1. Spin up admin UI + privacy center against this branch (paired with the fidesplus PR for the storage normalization)
  2. Create a property; configure an Access action with default identities + a custom text field "reason"
  3. In the form builder, drag "reason" between Email and Phone, save
  4. Open the public privacy center for that property → trigger the access form → confirm visual order is name → email → reason → phone
  5. Edit the same form, drag identities into a different sequence (e.g. email → name → phone), save and re-verify the public form picks up the new order
  6. Open a property created on demo/april (no field_order in its config), verify it loads in the new admin UI with the legacy order and saves cleanly with field_order present after save

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

🤖 Generated with Claude Code

Adds an optional `field_order: string[]` to PrivacyRequestOption so any
custom field can render between identity fields (e.g. a "reason" input
between Email and Phone). Previously the privacy center hardcoded the
sequence to name → email → phone → customs, so the form builder's
drag-and-drop ordering couldn't survive the round-trip back to the
legacy two-bucket shape (`identity_inputs` + `custom_privacy_request_fields`).

The form builder now emits `field_order` reflecting its children list,
the public privacy-center renderer iterates that list strictly when
present, and the existing `custom_privacy_request_field_order` is kept
as a backwards-compatible read fallback (deprecated). No DB migration —
the new field is additive on the JSONB blob.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Kelsey-Ethyca Kelsey-Ethyca requested review from a team as code owners May 6, 2026 19:51
@Kelsey-Ethyca Kelsey-Ethyca requested review from johnewart and jpople and removed request for a team May 6, 2026 19:51
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 6, 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 May 7, 2026 3:07am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
fides-privacy-center Ignored Ignored May 7, 2026 3:07am

Request Review

@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (demo/april@4c0d853). Learn more about missing BASE report.

Additional details and impacted files
@@              Coverage Diff              @@
##             demo/april    #8125   +/-   ##
=============================================
  Coverage              ?   84.95%           
=============================================
  Files                 ?      636           
  Lines                 ?    41586           
  Branches              ?     4836           
=============================================
  Hits                  ?    35330           
  Misses                ?     5157           
  Partials              ?     1099           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Kelsey-Ethyca and others added 2 commits May 6, 2026 16:15
- Rename test helper `describe_` to `stringifyField` and lift its
  declaration above first use (eslint: no-use-before-define,
  naming-convention, no-underscore-dangle).
- Apply `ruff format` to the schema module and tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Replace `_legacyOrder` destructure (eslint naming-convention rejects
  leading underscores) with a `delete` on a shallow copy of the action.
- Apply prettier auto-fix to `synthesize.ts` and `mapper.test.ts`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single-statement collapse caught by `prettier --check` in admin-ui
Clients-Unit. Auto-applied via `prettier --write`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

Title Lines Statements Branches Functions
admin-ui Coverage: 10%
8.27% (3828/46239) 7.3% (1933/26444) 5.7% (774/13571)
fides-js Coverage: 78%
79.39% (2011/2533) 65.99% (1240/1879) 73.09% (345/472)
privacy-center Coverage: 89%
87.26% (370/424) 82.84% (198/239) 80.76% (63/78)

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