mirror of
https://github.com/documenso/documenso.git
synced 2026-07-26 09:54:51 +10:00
chore: refine dropdown placeholder support in PDF fields documentation and implementation
This commit is contained in:
@@ -18,7 +18,7 @@ Do not introduce a new delimiter style. Existing applications may already genera
|
|||||||
- Keep the current placeholder grammar unchanged.
|
- Keep the current placeholder grammar unchanged.
|
||||||
- Support checkbox placeholders with option lists, checked values, validation, direction, required, read-only, and font size.
|
- Support checkbox placeholders with option lists, checked values, validation, direction, required, read-only, and font size.
|
||||||
- Support radio placeholders with option lists, default/preselected values, direction, required, read-only, and font size.
|
- Support radio placeholders with option lists, default/preselected values, direction, required, read-only, and font size.
|
||||||
- Support dropdown placeholders with option lists, placeholder text, default value, label, required, read-only, and font size.
|
- Support dropdown placeholders with option lists, default value, required, read-only, and font size.
|
||||||
- Use `options` as the only public list key in PDF placeholders.
|
- Use `options` as the only public list key in PDF placeholders.
|
||||||
- Convert `options` into internal `fieldMeta.values` during parsing.
|
- Convert `options` into internal `fieldMeta.values` during parsing.
|
||||||
- Make generated fields usable immediately in the editor, signing UI, preview renderer, and final PDF export.
|
- Make generated fields usable immediately in the editor, signing UI, preview renderer, and final PDF export.
|
||||||
@@ -38,8 +38,8 @@ Use the existing comma-separated placeholder format:
|
|||||||
{{checkbox, r1, options=Email|SMS|Phone, checked=Email|Phone, validationRule=atLeast, validationLength=1}}
|
{{checkbox, r1, options=Email|SMS|Phone, checked=Email|Phone, validationRule=atLeast, validationLength=1}}
|
||||||
{{radio, r1, options=Card|Bank transfer|Check, defaultValue=Check}}
|
{{radio, r1, options=Card|Bank transfer|Check, defaultValue=Check}}
|
||||||
{{radio, r1, options=Basic|Pro|Enterprise, selected=Pro, direction=horizontal}}
|
{{radio, r1, options=Basic|Pro|Enterprise, selected=Pro, direction=horizontal}}
|
||||||
{{dropdown, r1, label=Country, placeholder=Select country, options=United States|Canada|United Kingdom}}
|
{{dropdown, r1, options=United States|Canada|United Kingdom}}
|
||||||
{{dropdown, r2, label=Department, placeholder=Choose department, options=Sales|Legal|Finance, defaultValue=Legal}}
|
{{dropdown, r2, options=Sales|Legal|Finance, defaultValue=Legal}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `|` inside `options` because `,` is already the top-level placeholder delimiter.
|
Use `|` inside `options` because `,` is already the top-level placeholder delimiter.
|
||||||
@@ -101,6 +101,8 @@ Accepted keys:
|
|||||||
|
|
||||||
Map checkbox validation aliases internally: `atLeast` -> `Select at least`, `exactly` -> `Select exactly`, `atMost` -> `Select at most`.
|
Map checkbox validation aliases internally: `atLeast` -> `Select at least`, `exactly` -> `Select exactly`, `atMost` -> `Select at most`.
|
||||||
|
|
||||||
|
Checkbox placeholders do not support `label` or `placeholder` metadata.
|
||||||
|
|
||||||
### Radio
|
### Radio
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@@ -141,7 +143,7 @@ Radio placeholders do not support `label` or `placeholder` metadata.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
{{dropdown, r1, label=Department, placeholder=Choose department, options=Sales|Legal|Finance, defaultValue=Legal}}
|
{{dropdown, r1, options=Sales|Legal|Finance, defaultValue=Legal}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Normalize to:
|
Normalize to:
|
||||||
@@ -151,8 +153,6 @@ Normalize to:
|
|||||||
type: FieldType.DROPDOWN,
|
type: FieldType.DROPDOWN,
|
||||||
fieldMeta: {
|
fieldMeta: {
|
||||||
type: 'dropdown',
|
type: 'dropdown',
|
||||||
label: 'Department',
|
|
||||||
placeholder: 'Choose department',
|
|
||||||
values: [{ value: 'Sales' }, { value: 'Legal' }, { value: 'Finance' }],
|
values: [{ value: 'Sales' }, { value: 'Legal' }, { value: 'Finance' }],
|
||||||
defaultValue: 'Legal',
|
defaultValue: 'Legal',
|
||||||
},
|
},
|
||||||
@@ -161,8 +161,6 @@ Normalize to:
|
|||||||
|
|
||||||
Accepted keys:
|
Accepted keys:
|
||||||
|
|
||||||
- `label`
|
|
||||||
- `placeholder`
|
|
||||||
- `options`
|
- `options`
|
||||||
- `selected`, `default`, or `defaultValue`
|
- `selected`, `default`, or `defaultValue`
|
||||||
- `required=true|false`
|
- `required=true|false`
|
||||||
@@ -171,6 +169,8 @@ Accepted keys:
|
|||||||
|
|
||||||
`defaultValue` should only be set if it matches one parsed option.
|
`defaultValue` should only be set if it matches one parsed option.
|
||||||
|
|
||||||
|
Dropdown placeholders do not support `label` or `placeholder` metadata.
|
||||||
|
|
||||||
## Code Touchpoints
|
## Code Touchpoints
|
||||||
|
|
||||||
- `packages/lib/server-only/pdf/helpers.ts`
|
- `packages/lib/server-only/pdf/helpers.ts`
|
||||||
@@ -182,14 +182,7 @@ Accepted keys:
|
|||||||
- `packages/lib/types/field-meta.ts`
|
- `packages/lib/types/field-meta.ts`
|
||||||
- Keep current internal schemas: checkbox/radio/dropdown still store options as `fieldMeta.values`.
|
- Keep current internal schemas: checkbox/radio/dropdown still store options as `fieldMeta.values`.
|
||||||
- `packages/ui/primitives/document-flow/field-content.tsx`
|
- `packages/ui/primitives/document-flow/field-content.tsx`
|
||||||
- Display dropdown `fieldMeta.placeholder || Select` before insertion.
|
|
||||||
- Display a radio fallback when a placeholder-created radio has no options.
|
- Display a radio fallback when a placeholder-created radio has no options.
|
||||||
- `packages/lib/universal/field-renderer/render-dropdown-field.ts`
|
|
||||||
- Use dropdown placeholder text in edit/sign modes when no selected/default value exists.
|
|
||||||
- `apps/remix/app/components/general/document-signing/document-signing-dropdown-field.tsx`
|
|
||||||
- Use parsed dropdown placeholder text in `SelectValue`.
|
|
||||||
- `apps/remix/app/components/dialogs/sign-field-dropdown-dialog.tsx`
|
|
||||||
- Use the same placeholder text for the command input.
|
|
||||||
- Docs:
|
- Docs:
|
||||||
- `apps/docs/content/docs/users/documents/advanced/pdf-placeholders.mdx`
|
- `apps/docs/content/docs/users/documents/advanced/pdf-placeholders.mdx`
|
||||||
- `apps/docs/content/docs/developers/api/fields.mdx`
|
- `apps/docs/content/docs/developers/api/fields.mdx`
|
||||||
@@ -201,14 +194,13 @@ Unit tests:
|
|||||||
- `options=Yes|No|Maybe` becomes stable radio values.
|
- `options=Yes|No|Maybe` becomes stable radio values.
|
||||||
- `selected=No` marks only the matching radio option checked.
|
- `selected=No` marks only the matching radio option checked.
|
||||||
- Checkbox `options`, `checked`, `validationRule`, and `validationLength` normalize correctly.
|
- Checkbox `options`, `checked`, `validationRule`, and `validationLength` normalize correctly.
|
||||||
- Dropdown `placeholder`, `options`, and `defaultValue` normalize correctly.
|
- Dropdown `options` and `defaultValue` normalize correctly.
|
||||||
- Escaped delimiters parse correctly, for example `options=Sales\|Ops|Legal\, Compliance|A\=B`.
|
- Escaped delimiters parse correctly, for example `options=Sales\|Ops|Legal\, Compliance|A\=B`.
|
||||||
|
|
||||||
E2E/API tests:
|
E2E/API tests:
|
||||||
|
|
||||||
- Add a PDF fixture with checkbox, radio, and dropdown placeholders using the current syntax.
|
- Add a PDF fixture with checkbox, radio, and dropdown placeholders using the current syntax.
|
||||||
- Verify created fields have schema-compatible metadata and expected options/defaults.
|
- Verify created fields have schema-compatible metadata and expected options/defaults.
|
||||||
- Verify dropdown placeholder text appears before signing.
|
|
||||||
|
|
||||||
Suggested verification:
|
Suggested verification:
|
||||||
|
|
||||||
|
|||||||
@@ -113,11 +113,10 @@ You can customize fields by adding options after the recipient identifier:
|
|||||||
|
|
||||||
Checkbox, radio, and dropdown placeholders can define their selectable choices via the `options` property.
|
Checkbox, radio, and dropdown placeholders can define their selectable choices via the `options` property.
|
||||||
Separate choices with pipe (`|`) characters.
|
Separate choices with pipe (`|`) characters.
|
||||||
|
Checkbox, radio, and dropdown placeholders do not support `label` or `placeholder` metadata.
|
||||||
|
|
||||||
| Option | Applies To | Values | Description |
|
| Option | Applies To | Values | Description |
|
||||||
| ------------------ | ------------------------- | ------------------------ | ---------------------------------------- |
|
| ------------------ | ------------------------- | ------------------------ | ---------------------------------------- |
|
||||||
| `label` | Dropdown | Text | Label shown for the field |
|
|
||||||
| `placeholder` | Dropdown | Text | Text shown before a value is selected |
|
|
||||||
| `options` | Checkbox, Radio, Dropdown | `Option 1|Option 2` | Selectable choices |
|
| `options` | Checkbox, Radio, Dropdown | `Option 1|Option 2` | Selectable choices |
|
||||||
| `checked` | Checkbox | `Option 1|Option 2` | Pre-checked choices |
|
| `checked` | Checkbox | `Option 1|Option 2` | Pre-checked choices |
|
||||||
| `selected` | Radio, Dropdown | One option value | Pre-selected/default choice |
|
| `selected` | Radio, Dropdown | One option value | Pre-selected/default choice |
|
||||||
@@ -130,9 +129,10 @@ Separate choices with pipe (`|`) characters.
|
|||||||
| `readOnly` | Checkbox, Radio, Dropdown | `true`, `false` | Whether the pre-selected value is locked |
|
| `readOnly` | Checkbox, Radio, Dropdown | `true`, `false` | Whether the pre-selected value is locked |
|
||||||
| `fontSize` | Checkbox, Radio, Dropdown | Number (e.g., `12`) | Field text size |
|
| `fontSize` | Checkbox, Radio, Dropdown | Number (e.g., `12`) | Field text size |
|
||||||
|
|
||||||
For checkbox validation, `validationLength` defines the option count. `atLeast` means at
|
For checkbox validation, `validationLength` defines the option count:
|
||||||
least that many options, `exactly` means exactly that many options, and `atMost` means at
|
- `atLeast` means at least that many options must be selected
|
||||||
most that many options.
|
- `exactly` means exactly that many options must be selected
|
||||||
|
- `atMost` means at most that many options must be selected
|
||||||
|
|
||||||
If an option needs a literal delimiter, escape it with a backslash:
|
If an option needs a literal delimiter, escape it with a backslash:
|
||||||
|
|
||||||
@@ -149,8 +149,8 @@ If an option needs a literal delimiter, escape it with a backslash:
|
|||||||
{{text, r2, readOnly=true, text=Contract #12345}}
|
{{text, r2, readOnly=true, text=Contract #12345}}
|
||||||
{{checkbox, r1, options=Email|SMS|Phone, checked=Email|Phone, validationRule=atLeast, validationLength=1}}
|
{{checkbox, r1, options=Email|SMS|Phone, checked=Email|Phone, validationRule=atLeast, validationLength=1}}
|
||||||
{{radio, r1, options=Card|Bank transfer|Check, selected=Check}}
|
{{radio, r1, options=Card|Bank transfer|Check, selected=Check}}
|
||||||
{{dropdown, r1, label=Country, placeholder=Select country, options=United States|Canada|United Kingdom}}
|
{{dropdown, r1, options=United States|Canada|United Kingdom}}
|
||||||
{{dropdown, r2, label=Department, placeholder=Choose department, options=Sales|Legal|Finance, defaultValue=Legal}}
|
{{dropdown, r2, options=Sales|Legal|Finance, defaultValue=Legal}}
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ import type { FieldType, Recipient } from '@prisma/client';
|
|||||||
|
|
||||||
import { type TFieldAndMeta, ZEnvelopeFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
import { type TFieldAndMeta, ZEnvelopeFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
||||||
|
|
||||||
import { parseFieldMetaFromPlaceholder, parseFieldTypeFromPlaceholder } from './helpers';
|
import {
|
||||||
|
parseFieldMetaFromPlaceholder,
|
||||||
|
parseFieldTypeFromPlaceholder,
|
||||||
|
parsePlaceholderData,
|
||||||
|
parseRawFieldMetaFromPlaceholder,
|
||||||
|
} from './helpers';
|
||||||
|
|
||||||
const PLACEHOLDER_REGEX = /\{\{([^}]+)\}\}/g;
|
const PLACEHOLDER_REGEX = /\{\{([^}]+)\}\}/g;
|
||||||
const DEFAULT_FIELD_HEIGHT_PERCENT = 2;
|
const DEFAULT_FIELD_HEIGHT_PERCENT = 2;
|
||||||
@@ -89,7 +94,7 @@ export const extractPlaceholdersFromPDF = async (pdf: Buffer): Promise<Placehold
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const placeholderData = innerMatch[1].split(',').map((property) => property.trim());
|
const placeholderData = parsePlaceholderData(innerMatch[1]);
|
||||||
const [fieldTypeString, recipientOrMeta, ...fieldMetaData] = placeholderData;
|
const [fieldTypeString, recipientOrMeta, ...fieldMetaData] = placeholderData;
|
||||||
|
|
||||||
let fieldType: FieldType;
|
let fieldType: FieldType;
|
||||||
@@ -113,7 +118,7 @@ export const extractPlaceholdersFromPDF = async (pdf: Buffer): Promise<Placehold
|
|||||||
|
|
||||||
const recipient = recipientOrMeta;
|
const recipient = recipientOrMeta;
|
||||||
|
|
||||||
const rawFieldMeta = Object.fromEntries(fieldMetaData.map((property) => property.split('=')));
|
const rawFieldMeta = parseRawFieldMetaFromPlaceholder(fieldMetaData);
|
||||||
|
|
||||||
const parsedFieldMeta = parseFieldMetaFromPlaceholder(rawFieldMeta, fieldType);
|
const parsedFieldMeta = parseFieldMetaFromPlaceholder(rawFieldMeta, fieldType);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user