diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx index e57740cd1..43d40d41c 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-preview-page.tsx @@ -1,10 +1,20 @@ -import { lazy, useEffect, useState } from 'react'; +import { lazy, useEffect, useMemo, useState } from 'react'; +import { faker } from '@faker-js/faker/locale/en'; import { Trans } from '@lingui/react/macro'; -import { ConstructionIcon, FileTextIcon } from 'lucide-react'; +import { FieldType } from '@prisma/client'; +import { FileTextIcon } from 'lucide-react'; +import { match } from 'ts-pattern'; import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider'; -import { useCurrentEnvelopeRender } from '@documenso/lib/client-only/providers/envelope-render-provider'; +import { + EnvelopeRenderProvider, + useCurrentEnvelopeRender, +} from '@documenso/lib/client-only/providers/envelope-render-provider'; +import { ZFieldAndMetaSchema } from '@documenso/lib/types/field-meta'; +import { extractFieldInsertionValues } from '@documenso/lib/utils/envelope-signing'; +import { toCheckboxCustomText } from '@documenso/lib/utils/fields'; +import { extractInitials } from '@documenso/lib/utils/recipient-formatter'; import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out'; import PDFViewerKonvaLazy from '@documenso/ui/components/pdf-viewer/pdf-viewer-konva-lazy'; import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert'; @@ -15,15 +25,169 @@ import { EnvelopeRendererFileSelector } from './envelope-file-selector'; const EnvelopeGenericPageRenderer = lazy(async () => import('./envelope-generic-page-renderer')); +// Todo: Envelopes - Dynamically import faker export const EnvelopeEditorPreviewPage = () => { const { envelope, editorFields } = useCurrentEnvelopeEditor(); - const { currentEnvelopeItem } = useCurrentEnvelopeRender(); + const { currentEnvelopeItem, fields } = useCurrentEnvelopeRender(); const [selectedPreviewMode, setSelectedPreviewMode] = useState<'recipient' | 'signed'>( 'recipient', ); + const fieldsWithPlaceholders = useMemo(() => { + return fields.map((field) => { + const fieldMeta = ZFieldAndMetaSchema.parse(field); + + const recipient = envelope.recipients.find((recipient) => recipient.id === field.recipientId); + + if (!recipient) { + throw new Error('Recipient not found'); + } + + faker.seed(recipient.id); + + const recipientName = recipient.name || faker.person.fullName(); + const recipientEmail = recipient.email || faker.internet.email(); + + faker.seed(recipient.id + field.id); + + return { + ...field, + inserted: true, + ...match(fieldMeta) + .with({ type: FieldType.TEXT }, ({ fieldMeta }) => { + let text = fieldMeta?.text || faker.lorem.words(5); + + if (fieldMeta?.characterLimit) { + text = text.slice(0, fieldMeta?.characterLimit); + } + + return { + customText: text, + }; + }) + .with({ type: FieldType.NUMBER }, ({ fieldMeta }) => { + let number = fieldMeta?.value ?? ''; + + if (number === '') { + number = faker.number + .int({ + min: fieldMeta?.minValue ?? 0, + max: fieldMeta?.maxValue ?? 1000, + }) + .toString(); + } + + return { + customText: number, + }; + }) + .with({ type: FieldType.DATE }, () => { + const date = extractFieldInsertionValues({ + fieldValue: { + type: FieldType.DATE, + value: true, + }, + field, + documentMeta: envelope.documentMeta, + }); + + return { + customText: date.customText, + }; + }) + .with({ type: FieldType.EMAIL }, () => { + return { + customText: recipientEmail, + }; + }) + .with({ type: FieldType.NAME }, () => { + return { + customText: recipientName, + }; + }) + .with({ type: FieldType.INITIALS }, () => { + return { + customText: extractInitials(recipientName), + }; + }) + .with({ type: FieldType.RADIO }, ({ fieldMeta }) => { + const values = fieldMeta?.values ?? []; + + if (values.length === 0) { + return ''; + } + + let customText = ''; + + const preselectedValue = values.findIndex((value) => value.checked); + + if (preselectedValue !== -1) { + customText = preselectedValue.toString(); + } else { + const randomIndex = faker.number.int({ min: 0, max: values.length - 1 }); + customText = randomIndex.toString(); + } + + return { + customText, + }; + }) + .with({ type: FieldType.CHECKBOX }, ({ fieldMeta }) => { + let checkedValues: number[] = []; + + const values = fieldMeta?.values ?? []; + + values.forEach((value, index) => { + if (value.checked) { + checkedValues.push(index); + } + }); + + if (checkedValues.length === 0 && values.length > 0) { + const numberOfValues = fieldMeta?.validationLength || 1; + + checkedValues = Array.from({ length: numberOfValues }, (_, index) => index); + } + + return { + customText: toCheckboxCustomText(checkedValues), + }; + }) + .with({ type: FieldType.DROPDOWN }, ({ fieldMeta }) => { + const values = fieldMeta?.values ?? []; + + let customText = fieldMeta?.defaultValue || ''; + + if (!customText && values.length > 0) { + const randomIndex = faker.number.int({ min: 0, max: values.length - 1 }); + customText = values[randomIndex].value; + } + + return { + customText, + }; + }) + .with({ type: FieldType.SIGNATURE }, () => { + return { + customText: '', + signature: { + signatureImageAsBase64: '', + typedSignature: recipientName, + }, + }; + }) + .with({ type: FieldType.FREE_SIGNATURE }, () => { + return { + customText: '', + }; + }) + .exhaustive(), + }; + }); + }, [fields, envelope, envelope.recipients, envelope.documentMeta]); + /** * Set the selected recipient to the first recipient in the envelope. */ @@ -31,40 +195,38 @@ export const EnvelopeEditorPreviewPage = () => { editorFields.setSelectedRecipient(envelope.recipients[0]?.id ?? null); }, []); + // Override the parent renderer provider so we can inject custom fields. return ( -
-
- {/* Horizontal envelope item selector */} - + recipient.id)} + overrideSettings={{ + mode: 'export', + }} + > +
+
+ {/* Horizontal envelope item selector */} + - {/* Document View */} -
- - - Preview Mode - - - Preview what the signed document will look like with placeholder data - - + {/* Document View */} +
+ + + Preview Mode + + + Preview what the signed document will look like with placeholder data + + - {/* Coming soon section */} -
-
- -

- Coming soon -

-

- This feature is coming soon -

-
-
- - {/* Todo: Envelopes - Remove div after preview mode is implemented */} -
{currentEnvelopeItem !== null ? ( - + ) : (
@@ -78,27 +240,28 @@ export const EnvelopeEditorPreviewPage = () => { )}
-
- {/* Right Section - Form Fields Panel */} - {currentEnvelopeItem && false && ( -
- {/* Add fields section. */} -
- {/*

+ {/* Right Section - Form Fields Panel */} + {currentEnvelopeItem && false && ( +
+ {/* Add fields section. */} +
+ {/*

Preivew Mode

*/} - - - Preview Mode - - - Preview what the signed document will look like with placeholder data - - + + + Preview Mode + + + + Preview what the signed document will look like with placeholder data + + + - {/* + {/* {
Preview what a recipient will see
Preview the signed document
*/} -
+

- {false && ( - - {selectedPreviewMode === 'recipient' && ( - <> - + {false && ( + + {selectedPreviewMode === 'recipient' && ( + <> + - {/* Recipient selector section. */} -
-

- Selected Recipient -

+ {/* Recipient selector section. */} +
+

+ Selected Recipient +

- - editorFields.setSelectedRecipient(recipient.id) - } - recipients={envelope.recipients} - className="w-full" - align="end" - /> -
- - )} - - )} -
- )} -
+ + editorFields.setSelectedRecipient(recipient.id) + } + recipients={envelope.recipients} + className="w-full" + align="end" + /> + + + )} + + )} +
+ )} +
+
); }; diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx index e30c33c15..6593609f5 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx @@ -174,7 +174,7 @@ export const EnvelopeEditorSettingsDialog = ({ const { t, i18n } = useLingui(); const { toast } = useToast(); - const { envelope } = useCurrentEnvelopeEditor(); + const { envelope, updateEnvelopeAsync } = useCurrentEnvelopeEditor(); const team = useCurrentTeam(); const organisation = useCurrentOrganisation(); @@ -186,14 +186,12 @@ export const EnvelopeEditorSettingsDialog = ({ documentAuth: envelope.authOptions, }); - const form = useForm({ - resolver: zodResolver(ZAddSettingsFormSchema), - defaultValues: { - externalId: envelope.externalId || '', // Todo: String or undefined? + const createDefaultValues = () => { + return { + externalId: envelope.externalId || '', visibility: envelope.visibility || '', globalAccessAuth: documentAuthOption?.globalAccessAuth || [], globalActionAuth: documentAuthOption?.globalActionAuth || [], - meta: { subject: envelope.documentMeta.subject ?? '', message: envelope.documentMeta.message ?? '', @@ -210,10 +208,13 @@ export const EnvelopeEditorSettingsDialog = ({ emailSettings: ZDocumentEmailSettingsSchema.parse(envelope.documentMeta.emailSettings), signatureTypes: extractTeamSignatureSettings(envelope.documentMeta), }, - }, - }); + }; + }; - const { mutateAsync: updateEnvelope } = trpc.envelope.update.useMutation(); + const form = useForm({ + resolver: zodResolver(ZAddSettingsFormSchema), + defaultValues: createDefaultValues(), + }); const envelopeHasBeenSent = envelope.type === EnvelopeType.DOCUMENT && @@ -239,8 +240,7 @@ export const EnvelopeEditorSettingsDialog = ({ .safeParse(data.globalAccessAuth); try { - await updateEnvelope({ - envelopeId: envelope.id, + await updateEnvelopeAsync({ data: { externalId: data.externalId || null, visibility: data.visibility, @@ -295,7 +295,7 @@ export const EnvelopeEditorSettingsDialog = ({ ]); useEffect(() => { - form.reset(); + form.reset(createDefaultValues()); setActiveTab('general'); }, [open, form]); diff --git a/apps/remix/app/components/general/envelope-editor/envelope-generic-page-renderer.tsx b/apps/remix/app/components/general/envelope-editor/envelope-generic-page-renderer.tsx index 74635816b..37f7cd5b6 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-generic-page-renderer.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-generic-page-renderer.tsx @@ -12,7 +12,7 @@ import { getClientSideFieldTranslations } from '@documenso/lib/utils/fields'; export default function EnvelopeGenericPageRenderer() { const { i18n } = useLingui(); - const { currentEnvelopeItem, fields, getRecipientColorKey, setRenderError } = + const { currentEnvelopeItem, fields, getRecipientColorKey, setRenderError, overrideSettings } = useCurrentEnvelopeRender(); const { @@ -50,12 +50,11 @@ export default function EnvelopeGenericPageRenderer() { field: { renderId: field.id.toString(), ...field, - customText: '', width: Number(field.width), height: Number(field.height), positionX: Number(field.positionX), positionY: Number(field.positionY), - inserted: false, + customText: field.inserted ? field.customText : '', fieldMeta: field.fieldMeta, }, translations: getClientSideFieldTranslations(i18n), @@ -63,7 +62,7 @@ export default function EnvelopeGenericPageRenderer() { pageHeight: unscaledViewport.height, color: getRecipientColorKey(field.recipientId), editable: false, - mode: 'sign', + mode: overrideSettings?.mode ?? 'sign', }); }; diff --git a/apps/remix/package.json b/apps/remix/package.json index 7e3c00771..3454f3285 100644 --- a/apps/remix/package.json +++ b/apps/remix/package.json @@ -25,6 +25,7 @@ "@documenso/trpc": "*", "@documenso/ui": "*", "@epic-web/remember": "^1.1.0", + "@faker-js/faker": "^10.1.0", "@hono/node-server": "^1.13.7", "@hono/trpc-server": "^0.3.4", "@hookform/resolvers": "^3.1.0", @@ -104,4 +105,4 @@ "vite-tsconfig-paths": "^5.1.4" }, "version": "1.13.1" -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index e0d18f26d..783d6865f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -113,6 +113,7 @@ "@documenso/trpc": "*", "@documenso/ui": "*", "@epic-web/remember": "^1.1.0", + "@faker-js/faker": "^10.1.0", "@hono/node-server": "^1.13.7", "@hono/trpc-server": "^0.3.4", "@hookform/resolvers": "^3.1.0", @@ -3512,6 +3513,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-10.1.0.tgz", + "integrity": "sha512-C3mrr3b5dRVlKPJdfrAXS8+dq+rq8Qm5SNRazca0JKgw1HQERFmrVb0towvMmw5uu8hHKNiQasMaR/tydf3Zsg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0", + "npm": ">=10" + } + }, "node_modules/@floating-ui/core": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", diff --git a/packages/app-tests/e2e/envelopes/envelope-alignment.spec.ts b/packages/app-tests/e2e/envelopes/envelope-alignment.spec.ts index 4644be247..a2a1bb2b1 100644 --- a/packages/app-tests/e2e/envelopes/envelope-alignment.spec.ts +++ b/packages/app-tests/e2e/envelopes/envelope-alignment.spec.ts @@ -25,8 +25,7 @@ import { DocumentStatus } from '@prisma/client'; import fs from 'node:fs'; import path from 'node:path'; import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.js'; - -import { getFile } from '@documenso/lib/universal/upload/get-file'; +import { getEnvelopeDownloadUrl } from '@documenso/lib/utils/envelope-download'; import { prisma } from '@documenso/prisma'; import { seedAlignmentTestDocument } from '@documenso/prisma/seed/initial-seed'; import { seedUser } from '@documenso/prisma/seed/users'; @@ -95,7 +94,13 @@ test('field placement visual regression', async ({ page }, testInfo) => { await Promise.all( completedDocument.envelopeItems.map(async (item) => { - const pdfData = await getFile(item.documentData); + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: item, + token, + version: 'signed', + }); + + const pdfData = await fetch(documentUrl).then(async (res) => await res.arrayBuffer()); const loadedImages = storedImages .filter((image) => image.includes(item.title)) @@ -103,7 +108,7 @@ test('field placement visual regression', async ({ page }, testInfo) => { await compareSignedPdfWithImages({ id: item.title.replaceAll(' ', '-').toLowerCase(), - pdfData, + pdfData: new Uint8Array(pdfData), images: loadedImages, testInfo, }); @@ -174,9 +179,15 @@ test.skip('download envelope images', async ({ page }) => { await Promise.all( completedDocument.envelopeItems.map(async (item) => { - const pdfData = await getFile(item.documentData); + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: item, + token, + version: 'signed', + }); - const pdfImages = await renderPdfToImage(pdfData); + const pdfData = await fetch(documentUrl).then(async (res) => await res.arrayBuffer()); + + const pdfImages = await renderPdfToImage(new Uint8Array(pdfData)); for (const [index, { image }] of pdfImages.entries()) { fs.writeFileSync( diff --git a/packages/app-tests/e2e/features/include-document-certificate.spec.ts b/packages/app-tests/e2e/features/include-document-certificate.spec.ts index 961c6fb3e..ec5ee5fda 100644 --- a/packages/app-tests/e2e/features/include-document-certificate.spec.ts +++ b/packages/app-tests/e2e/features/include-document-certificate.spec.ts @@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test'; import { DocumentStatus, FieldType } from '@prisma/client'; import { getDocumentByToken } from '@documenso/lib/server-only/document/get-document-by-token'; -import { getFile } from '@documenso/lib/universal/upload/get-file'; +import { getEnvelopeDownloadUrl } from '@documenso/lib/utils/envelope-download'; import { prisma } from '@documenso/prisma'; import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents'; import { seedTeam } from '@documenso/prisma/seed/teams'; @@ -25,20 +25,25 @@ test.describe('Signing Certificate Tests', () => { teamId: team.id, }); - const documentData = await prisma.documentData + const recipient = recipients[0]; + + const documentData = await prisma.envelopeItem .findFirstOrThrow({ where: { - envelopeItem: { - envelopeId: document.id, - }, + envelopeId: document.id, }, }) - .then(async (data) => getFile(data)); + .then(async (data) => { + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: data, + token: recipient.token, + version: 'signed', + }); + return fetch(documentUrl).then(async (res) => await res.arrayBuffer()); + }); const originalPdf = await PDFDocument.load(documentData); - const recipient = recipients[0]; - // Sign the document await page.goto(`/sign/${recipient.token}`); @@ -78,9 +83,17 @@ test.describe('Signing Certificate Tests', () => { }, }); - const firstDocumentData = completedDocument.envelopeItems[0].documentData; + const firstDocumentData = completedDocument.envelopeItems[0]; - const completedDocumentData = await getFile(firstDocumentData); + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: firstDocumentData, + token: recipient.token, + version: 'signed', + }); + + const pdfData = await fetch(documentUrl).then(async (res) => await res.arrayBuffer()); + + const completedDocumentData = new Uint8Array(pdfData); // Load the PDF and check number of pages const pdfDoc = await PDFDocument.load(completedDocumentData); @@ -117,20 +130,25 @@ test.describe('Signing Certificate Tests', () => { }, }); - const documentData = await prisma.documentData + const recipient = recipients[0]; + + const documentData = await prisma.envelopeItem .findFirstOrThrow({ where: { - envelopeItem: { - envelopeId: document.id, - }, + envelopeId: document.id, }, }) - .then(async (data) => getFile(data)); + .then(async (data) => { + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: data, + token: recipient.token, + version: 'signed', + }); + return fetch(documentUrl).then(async (res) => await res.arrayBuffer()); + }); const originalPdf = await PDFDocument.load(documentData); - const recipient = recipients[0]; - // Sign the document await page.goto(`/sign/${recipient.token}`); @@ -168,9 +186,17 @@ test.describe('Signing Certificate Tests', () => { }, }); - const firstDocumentData = completedDocument.envelopeItems[0].documentData; + const firstDocumentData = completedDocument.envelopeItems[0]; - const completedDocumentData = await getFile(firstDocumentData); + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: firstDocumentData, + token: recipient.token, + version: 'signed', + }); + + const pdfData = await fetch(documentUrl).then(async (res) => await res.arrayBuffer()); + + const completedDocumentData = new Uint8Array(pdfData); // Load the PDF and check number of pages const completedPdf = await PDFDocument.load(completedDocumentData); @@ -207,19 +233,24 @@ test.describe('Signing Certificate Tests', () => { }, }); - const documentData = await prisma.documentData + const recipient = recipients[0]; + + const documentData = await prisma.envelopeItem .findFirstOrThrow({ where: { - envelopeItem: { - envelopeId: document.id, - }, + envelopeId: document.id, }, }) - .then(async (data) => getFile(data)); + .then(async (data) => { + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: data, + token: recipient.token, + version: 'signed', + }); + return fetch(documentUrl).then(async (res) => await res.arrayBuffer()); + }); - const originalPdf = await PDFDocument.load(documentData); - - const recipient = recipients[0]; + const originalPdf = await PDFDocument.load(new Uint8Array(documentData)); // Sign the document await page.goto(`/sign/${recipient.token}`); @@ -258,7 +289,15 @@ test.describe('Signing Certificate Tests', () => { }, }); - const completedDocumentData = await getFile(completedDocument.envelopeItems[0].documentData); + const documentUrl = getEnvelopeDownloadUrl({ + envelopeItem: completedDocument.envelopeItems[0], + token: recipient.token, + version: 'signed', + }); + + const completedDocumentData = await fetch(documentUrl).then( + async (res) => await res.arrayBuffer(), + ); // Load the PDF and check number of pages const completedPdf = await PDFDocument.load(completedDocumentData); diff --git a/packages/lib/client-only/providers/envelope-editor-provider.tsx b/packages/lib/client-only/providers/envelope-editor-provider.tsx index baf216ac3..dca7c68bc 100644 --- a/packages/lib/client-only/providers/envelope-editor-provider.tsx +++ b/packages/lib/client-only/providers/envelope-editor-provider.tsx @@ -46,6 +46,7 @@ type EnvelopeEditorProviderValue = { setLocalEnvelope: (localEnvelope: Partial) => void; updateEnvelope: (envelopeUpdates: UpdateEnvelopePayload) => void; + updateEnvelopeAsync: (envelopeUpdates: UpdateEnvelopePayload) => Promise; setRecipientsDebounced: (recipients: TSetEnvelopeRecipientsRequest['recipients']) => void; setRecipientsAsync: (recipients: TSetEnvelopeRecipientsRequest['recipients']) => Promise; @@ -66,8 +67,6 @@ type EnvelopeEditorProviderValue = { }; syncEnvelope: () => Promise; - // refetchEnvelope: () => Promise; - // updateEnvelope: (envelope: TEnvelope) => Promise; }; interface EnvelopeEditorProviderProps { @@ -236,6 +235,13 @@ export const EnvelopeEditorProvider = ({ setEnvelopeDebounced(envelopeUpdates); }; + const updateEnvelopeAsync = async (envelopeUpdates: UpdateEnvelopePayload) => { + await envelopeUpdateMutationQuery.mutateAsync({ + envelopeId: envelope.id, + ...envelopeUpdates, + }); + }; + const getRecipientColorKey = useCallback( (recipientId: number) => { const recipientIndex = envelope.recipients.findIndex( @@ -323,6 +329,7 @@ export const EnvelopeEditorProvider = ({ setLocalEnvelope, getRecipientColorKey, updateEnvelope, + updateEnvelopeAsync, setRecipientsDebounced, setRecipientsAsync, editorFields, diff --git a/packages/lib/client-only/providers/envelope-render-provider.tsx b/packages/lib/client-only/providers/envelope-render-provider.tsx index 0523678e4..12fa53003 100644 --- a/packages/lib/client-only/providers/envelope-render-provider.tsx +++ b/packages/lib/client-only/providers/envelope-render-provider.tsx @@ -16,6 +16,10 @@ type FileData = status: 'loaded'; }; +type EnvelopeRenderOverrideSettings = { + mode: 'edit' | 'sign' | 'export'; +}; + type EnvelopeRenderItem = TEnvelope['envelopeItems'][number]; type EnvelopeRenderProviderValue = { @@ -28,10 +32,12 @@ type EnvelopeRenderProviderValue = { renderError: boolean; setRenderError: (renderError: boolean) => void; + overrideSettings?: EnvelopeRenderOverrideSettings; }; interface EnvelopeRenderProviderProps { children: React.ReactNode; + envelope: Pick; /** @@ -54,6 +60,11 @@ interface EnvelopeRenderProviderProps { * If not provided, it will be assumed that the current user can access the document. */ token: string | undefined; + + /** + * Custom override settings for generic page renderers. + */ + overrideSettings?: EnvelopeRenderOverrideSettings; } const EnvelopeRenderContext = createContext(null); @@ -77,6 +88,7 @@ export const EnvelopeRenderProvider = ({ fields, token, recipientIds = [], + overrideSettings, }: EnvelopeRenderProviderProps) => { // Indexed by documentDataId. const [files, setFiles] = useState>({}); @@ -185,6 +197,7 @@ export const EnvelopeRenderProvider = ({ getRecipientColorKey, renderError, setRenderError, + overrideSettings, }} > {children} diff --git a/packages/ui/primitives/combobox.tsx b/packages/ui/primitives/combobox.tsx index 2f5bb6712..038b5f48a 100644 --- a/packages/ui/primitives/combobox.tsx +++ b/packages/ui/primitives/combobox.tsx @@ -58,7 +58,7 @@ const Combobox = ({ - +