feat: multi-select bulk editing for document fields#32
Conversation
Adds a bulk-edit panel that appears when two or more fields are selected (via ctrl/cmd/shift-click) in the document and template field editors. The panel lets users change shared properties — field type, font size, text alignment, required and read-only — across the whole selection at once. Each change applies immediately to every selected field, with "Mixed" indicators when values differ. Type conversions reuse the new type's defaults while carrying over compatible shared properties. Selection is already visually indicated by the primary ring on each selected field. The selection is cleared when switching recipients so the panel never targets another recipient's fields. Also fixes a pre-existing flow-analysis error in auto-sized-text where newFontSize could be used before assignment. Closes #15 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a bulk field settings feature, allowing users to edit properties like type, font size, and alignment across multiple selected fields simultaneously in both document and template flows. It also includes a fix for AutoSizedText to prevent undefined font sizes. A high-severity issue was identified in the bulk settings logic where using parse instead of safeParse on schema validation could cause the UI to crash during intermediate input states.
| } | ||
| } | ||
|
|
||
| const fieldMeta: TFieldMetaSchema = candidate ? ZFieldMetaSchema.parse(candidate) : undefined; |
There was a problem hiding this comment.
Using ZFieldMetaSchema.parse here will throw an exception if the candidate object is invalid according to the schema. This is likely to happen during user input, for example, when a user is typing a font size and the value is temporarily outside the allowed range (8-96). A crash in this mapping function will break the UI and prevent further updates.
Consider using safeParse and falling back to the raw candidate object if parsing fails. This allows the form state to hold the intermediate invalid value (ensuring a smooth UX without flickering) while the form's overall validation (triggered in handleAutoSave) will correctly prevent the invalid data from being persisted.
const parsed = candidate ? ZFieldMetaSchema.safeParse(candidate) : null;
const fieldMeta: TFieldMetaSchema = parsed
? parsed.success
? parsed.data
: (candidate as TFieldMetaSchema)
: undefined;
Playwright Validation Evidence (PR #32)Screenshots from automated validation against
Bundle verificationThe PR's bulk-edit panel ships in the deployed build — (all found in NoteThe headless Playwright drag-to-place flow was thwarted in this environment — the Add Fields page renders the PDF page as an Automated Playwright screenshots from https://documenso-dev.psd401.net (image |
reeseherber
left a comment
There was a problem hiding this comment.
VALIDATION FAIL
What I verified
- PR branch (
feat/multi-select-field-editing, single commit66fbb976) built and deployed on the dev server (imagedocumenso-pr32:test, recreated fromghcr.io/psd401/documenso:latest). - Site reachable,
pipeline@psd401.netemail/password sign-in works. - Reviewed
gh pr diff 32against the acceptance criteria — newBulkFieldSettingspanel +applyBulkSettingsToFieldhelper wired into bothadd-fields.tsxandadd-template-fields.tsx; selection-clearing-on-recipient-switch effect in place;auto-sized-text.tsxflow-analysis fix is reasonable. - All nine new
data-testidmarkers frombulk-field-settings.tsxappear in the deployed JS bundle (/app/apps/remix/build/client/assets/legacy-field-warning-popover-*.js).
Why I'm calling FAIL
Gemini's high-severity comment on packages/ui/primitives/document-flow/bulk-field-settings.tsx:148 is correct, the PR was updated since then, but the fix was not applied.
const fieldMeta: TFieldMetaSchema = candidate ? ZFieldMetaSchema.parse(candidate) : undefined;This runs on every bulk update. The font-size <Input type="number" min={8} max={96}> calls onApply({ fontSize: parsed }) from onChange for any numeric value the user types — min/max on the input are hints, not gates. The schema is z.number().min(8).max(96), so as soon as the user clears the input and types 7 (or anything past 96), ZFieldMetaSchema.parse(...) throws a ZodError mid-render of the bulk panel, which propagates to the nearest error boundary and tears the editor out from under the user. Same hazard if any other future caller passes an unvalidated value.
The suggested swap to safeParse with fallback to the raw candidate keeps the form state holding the intermediate invalid value (so the input doesn't fight the user mid-keystroke) and leaves the form's outer handleAutoSave validation to refuse persisting it — exactly the behaviour every other meta-editor in the codebase relies on. Please apply the suggested fix (or equivalent) and I'll re-validate.
What I could not finish, and why
I wanted to drive the panel end-to-end in Playwright (multi-select two fields, change font size including out-of-range to confirm the crash, change type, toggle required/read-only). On this dev environment the editor renders the PDF page as <img class="react-pdf__Page"> with a Konva <canvas> overlay on top, so document.elementFromPoint(...) at the drop target returns the Konva canvas, not a descendant of .react-pdf__Page. Documenso's onMouseClick (packages/ui/primitives/document-flow/add-fields.tsx:297) clears selectedField but append never fires, so no FieldItem gets placed and the bulk panel can't be shown. That's an environment issue independent of this PR — add-fields.tsx was not touched in a way that would change placement — but it kept me from producing a live screenshot of the panel itself. Evidence comment with screenshots: posted alongside this review.
Verdict
FAIL — please address the ZFieldMetaSchema.parse → safeParse change Gemini flagged. Everything else looks good.






Closes #15
Summary
Adds a bulk-edit panel for editing multiple document fields at once, in both the document and template field editors.
What changed
The panel is a draggable/collapsible floating panel matching the existing single-field advanced-settings panel, and reuses the existing selection state (
useFieldDeletion) and per-field selection ring for visual indication.Acceptance criteria
Implementation notes
packages/ui/primitives/document-flow/bulk-field-settings.tsx(BulkFieldSettings+applyBulkSettingsToFieldhelper). The helper rebuilds each field's meta as a plain object and validates it throughZFieldMetaSchema, so the result is correctly narrowed to the discriminated meta union.add-fields.tsx(documents) andadd-template-fields.tsx(templates).auto-sized-text.tsxwherenewFontSizecould be used before assignment.Testing
npx tsc --noEmitpasses for the@documenso/uipackage (0 errors).🤖 Generated with Claude Code