From 22204e04c8168c8aeb69e67893f65076a6bbf963 Mon Sep 17 00:00:00 2001 From: aorlov Date: Thu, 5 Feb 2026 22:38:00 +0100 Subject: [PATCH 1/2] feat: (esign) introduce viewOptions for document layout and deprecate layoutMode and layoutMargins - Added `viewOptions` to control document layout with options for 'web' and 'print'. - Deprecated `layoutMode` and `layoutMargins`, recommending CSS for margin control. - Updated documentation and examples to reflect the new configuration. --- apps/docs/solutions/esign/api-reference.mdx | 41 ++++++++-- apps/docs/solutions/esign/configuration.mdx | 33 +++++--- packages/esign/demo/src/App.tsx | 3 +- packages/esign/package.json | 2 +- .../src/__tests__/SuperDocESign.test.tsx | 80 ++++++++++++++++++- packages/esign/src/index.tsx | 17 +--- packages/esign/src/test/setup.ts | 9 +++ packages/esign/src/types.ts | 15 +++- 8 files changed, 161 insertions(+), 39 deletions(-) diff --git a/apps/docs/solutions/esign/api-reference.mdx b/apps/docs/solutions/esign/api-reference.mdx index cbfa32470c..6d909c5e29 100644 --- a/apps/docs/solutions/esign/api-reference.mdx +++ b/apps/docs/solutions/esign/api-reference.mdx @@ -24,14 +24,28 @@ Complete eSign component API Display mode: `'full' | 'download'` - - Layout mode: `'paginated' | 'responsive'` + + View options for controlling document layout (recommended) + + + + Document layout: `'print'` (default) or `'web'` + - `print` - Fixed page width, displays document as it prints + - `web` - Content reflows to fit container (mobile/accessibility) + + - - Custom margins (pixels) for `layoutMode: 'responsive'` + + **Deprecated.** Use `viewOptions.layout` instead. + - `'paginated'` → `viewOptions: { layout: 'print' }` + - `'responsive'` → `viewOptions: { layout: 'web' }` - + + + **Deprecated.** No longer supported. Use CSS for margin control. + + Require scrolling to bottom before signing @@ -235,9 +249,19 @@ interface AuditEvent { } ``` -### LayoutMargins +### ViewOptions + +Document view options (recommended): + +```typescript +interface ViewOptions { + layout?: 'web' | 'print'; // 'print' is default +} +``` + +### LayoutMargins (Deprecated) -Custom margins for responsive layout mode: +Deprecated since v2.0. Use CSS for margin control. ```typescript interface LayoutMargins { @@ -345,6 +369,7 @@ All types are exported: import type { SuperDocESignProps, SuperDocESignHandle, + DocumentConfig, SubmitData, DownloadData, SigningState, @@ -355,7 +380,7 @@ import type { FieldChange, AuditEvent, FieldComponentProps, - LayoutMargins + LayoutMargins // deprecated } from '@superdoc-dev/esign'; ``` diff --git a/apps/docs/solutions/esign/configuration.mdx b/apps/docs/solutions/esign/configuration.mdx index 5841056a33..1c63ea43b0 100644 --- a/apps/docs/solutions/esign/configuration.mdx +++ b/apps/docs/solutions/esign/configuration.mdx @@ -16,9 +16,8 @@ document={{ // Display mode mode: 'full', // 'full' (default) or 'download' - // Layout mode - layoutMode: 'responsive', // 'paginated' (default) or 'responsive' - layoutMargins: { top: 10, bottom: 10, left: 10, right: 10 }, + // View options (recommended) + viewOptions: { layout: 'web' }, // 'print' (default) or 'web' // Requirements validation: { @@ -27,14 +26,29 @@ document={{ }} ``` -### Layout modes +### View options -Control how the document is displayed: +Control how the document is displayed using `viewOptions.layout`: -- **`paginated`** (default) - Fixed page width with page breaks -- **`responsive`** - 100% width, text reflows to fit container +- **`print`** (default) - Fixed page width, displays document as it prints +- **`web`** - Content reflows to fit container width (mobile/accessibility) -Use `responsive` when you want text to reflow to fit the container (useful for mobile/accessibility). Optional `layoutMargins` sets custom margins (in pixels) for responsive mode. +Use `web` when you want text to reflow to fit the container. This is recommended for mobile devices and WCAG AA reflow compliance (Success Criterion 1.4.10). + +```jsx +// Mobile-friendly responsive layout +document={{ + source: contractFile, + viewOptions: { layout: 'web' } +}} +``` + + +**Deprecated options**: `layoutMode` and `layoutMargins` are deprecated since v2.0. Use `viewOptions` instead: +- `layoutMode: 'responsive'` → `viewOptions: { layout: 'web' }` +- `layoutMode: 'paginated'` → `viewOptions: { layout: 'print' }` +- `layoutMargins` is no longer supported. Use CSS to control margins. + When `mode` is set to `download`, the submit button is hidden. @@ -340,8 +354,7 @@ Style the component to match your brand. document={{ source: contractFile, mode: "full", - layoutMode: "responsive", - layoutMargins: { top: 10, bottom: 10, left: 10, right: 10 }, + viewOptions: { layout: "web" }, validation: { scroll: { required: true }, }, diff --git a/packages/esign/demo/src/App.tsx b/packages/esign/demo/src/App.tsx index 18b7e9413b..247493b91c 100644 --- a/packages/esign/demo/src/App.tsx +++ b/packages/esign/demo/src/App.tsx @@ -330,8 +330,7 @@ export function App() { document={{ source: documentSource, mode: 'full', - layoutMode: 'responsive', - layoutMargins: { top: 10, bottom: 10, left: 10, right: 10 }, + viewOptions: { layout: 'web' }, validation: { scroll: { required: true }, }, diff --git a/packages/esign/package.json b/packages/esign/package.json index 37ac9ad218..9a6db22638 100644 --- a/packages/esign/package.json +++ b/packages/esign/package.json @@ -1,6 +1,6 @@ { "name": "@superdoc-dev/esign", - "version": "1.3.0", + "version": "2.0.0", "description": "React eSignature component for SuperDoc", "type": "module", "main": "./dist/index.js", diff --git a/packages/esign/src/__tests__/SuperDocESign.test.tsx b/packages/esign/src/__tests__/SuperDocESign.test.tsx index f256ace32f..bbee6de7ba 100644 --- a/packages/esign/src/__tests__/SuperDocESign.test.tsx +++ b/packages/esign/src/__tests__/SuperDocESign.test.tsx @@ -8,7 +8,12 @@ import SuperDocESign from '../index'; import type { FieldComponentProps, SuperDocESignHandle, SuperDocESignProps, AuditEvent } from '../types'; import { SuperDoc } from 'superdoc'; -import { getAuditEventTypes, resetAuditEvents } from '../test/setup'; +import { + getAuditEventTypes, + resetAuditEvents, + getLastConstructorOptions, + resetLastConstructorOptions, +} from '../test/setup'; const scrollListeners = new WeakMap(); const originalAddEventListener = HTMLElement.prototype.addEventListener; @@ -144,6 +149,7 @@ beforeEach(() => { superDocMock.mockGetStructuredContentTablesById.mockReturnValue([]); superDocMock.mockDestroy.mockReset(); resetAuditEvents(); + resetLastConstructorOptions(); }); describe('SuperDocESign component', () => { @@ -724,4 +730,76 @@ describe('SuperDocESign component', () => { ]); }); }); + + describe('viewOptions configuration', () => { + it('passes viewOptions directly to SuperDoc when provided', async () => { + renderComponent({ + document: { + source: '

Test

', + viewOptions: { layout: 'web' }, + }, + }); + + await waitForSuperDocReady(); + + const options = getLastConstructorOptions(); + expect(options.viewOptions).toEqual({ layout: 'web' }); + }); + + it('translates layoutMode "responsive" to viewOptions layout "web"', async () => { + renderComponent({ + document: { + source: '

Test

', + layoutMode: 'responsive', + }, + }); + + await waitForSuperDocReady(); + + const options = getLastConstructorOptions(); + expect(options.viewOptions).toEqual({ layout: 'web' }); + }); + + it('translates layoutMode "paginated" to viewOptions layout "print"', async () => { + renderComponent({ + document: { + source: '

Test

', + layoutMode: 'paginated', + }, + }); + + await waitForSuperDocReady(); + + const options = getLastConstructorOptions(); + expect(options.viewOptions).toEqual({ layout: 'print' }); + }); + + it('defaults to viewOptions layout "print" when neither viewOptions nor layoutMode is specified', async () => { + renderComponent({ + document: { + source: '

Test

', + }, + }); + + await waitForSuperDocReady(); + + const options = getLastConstructorOptions(); + expect(options.viewOptions).toEqual({ layout: 'print' }); + }); + + it('prefers viewOptions over deprecated layoutMode when both are provided', async () => { + renderComponent({ + document: { + source: '

Test

', + viewOptions: { layout: 'print' }, + layoutMode: 'responsive', + }, + }); + + await waitForSuperDocReady(); + + const options = getLastConstructorOptions(); + expect(options.viewOptions).toEqual({ layout: 'print' }); + }); + }); }); diff --git a/packages/esign/src/index.tsx b/packages/esign/src/index.tsx index a13e2998bb..d855aaee75 100644 --- a/packages/esign/src/index.tsx +++ b/packages/esign/src/index.tsx @@ -209,9 +209,9 @@ const SuperDocESign = forwardRef { // Guard callback execution if cleanup already ran if (aborted) return; @@ -238,16 +238,7 @@ const SuperDocESign = forwardRef { if (!document.validation?.scroll?.required || !isReady) return; diff --git a/packages/esign/src/test/setup.ts b/packages/esign/src/test/setup.ts index 623536d3c6..258a52b952 100644 --- a/packages/esign/src/test/setup.ts +++ b/packages/esign/src/test/setup.ts @@ -46,7 +46,11 @@ const mockEditor = { }, }; +let lastConstructorOptions: any = null; + const SuperDocMock = vi.fn((options: any = {}) => { + lastConstructorOptions = options; + if (options?.onReady) { if (typeof queueMicrotask === 'function') { queueMicrotask(() => options.onReady()); @@ -62,6 +66,11 @@ const SuperDocMock = vi.fn((options: any = {}) => { }; }); +export const getLastConstructorOptions = () => lastConstructorOptions; +export const resetLastConstructorOptions = () => { + lastConstructorOptions = null; +}; + (SuperDocMock as any).mockEditor = mockEditor; (SuperDocMock as any).mockUpdateStructuredContentById = mockUpdateStructuredContentById; (SuperDocMock as any).mockGetStructuredContentTags = mockGetStructuredContentTags; diff --git a/packages/esign/src/types.ts b/packages/esign/src/types.ts index 7fdebd78b0..9c92117b7b 100644 --- a/packages/esign/src/types.ts +++ b/packages/esign/src/types.ts @@ -72,15 +72,22 @@ export interface DocumentConfig { }; }; /** + * Document view options (recommended for v1.x): + * - 'print': Fixed page width, displays document as it prints (default) + * - 'web': Content reflows to fit container width (mobile/accessibility) + */ + viewOptions?: { + layout?: 'web' | 'print'; + }; + /** + * @deprecated Use `viewOptions: { layout: 'web' }` instead. * Document layout mode: * - 'paginated' (default): Fixed page width, shows page breaks - * - 'responsive': 100% width, text reflows to fit container (useful for mobile/accessibility) - * Note: 'responsive' takes precedence over pagination - pagination is ignored when layoutMode is 'responsive' + * - 'responsive': 100% width, text reflows to fit container */ layoutMode?: 'responsive' | 'paginated'; /** - * Custom margins in pixels for responsive layout mode. - * Only applies when layoutMode is 'responsive'. + * @deprecated No longer supported in v1.x. Use CSS for margin control. */ layoutMargins?: LayoutMargins; } From ab81ddac955bb42bf35860f09b8cd3a8b9159531 Mon Sep 17 00:00:00 2001 From: aorlov Date: Fri, 6 Feb 2026 01:35:43 +0100 Subject: [PATCH 2/2] fix: update viewOptions handling in SuperDocESign component - Improved the logic for setting `viewOptions` to ensure it falls back to `layoutMode` when `viewOptions` is an empty object. - Updated the documentation to clarify the recommended usage of `viewOptions` for SuperDoc versions greater than 1.6.x. - Added a test case to verify the fallback behavior for layout options. --- .../esign/src/__tests__/SuperDocESign.test.tsx | 15 +++++++++++++++ packages/esign/src/index.tsx | 8 ++++---- packages/esign/src/types.ts | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/esign/src/__tests__/SuperDocESign.test.tsx b/packages/esign/src/__tests__/SuperDocESign.test.tsx index bbee6de7ba..dc7aae0258 100644 --- a/packages/esign/src/__tests__/SuperDocESign.test.tsx +++ b/packages/esign/src/__tests__/SuperDocESign.test.tsx @@ -801,5 +801,20 @@ describe('SuperDocESign component', () => { const options = getLastConstructorOptions(); expect(options.viewOptions).toEqual({ layout: 'print' }); }); + + it('falls back to layoutMode when viewOptions is empty object', async () => { + renderComponent({ + document: { + source: '

Test

', + viewOptions: {}, + layoutMode: 'responsive', + }, + }); + + await waitForSuperDocReady(); + + const options = getLastConstructorOptions(); + expect(options.viewOptions).toEqual({ layout: 'web' }); + }); }); }); diff --git a/packages/esign/src/index.tsx b/packages/esign/src/index.tsx index d855aaee75..98cbeca791 100644 --- a/packages/esign/src/index.tsx +++ b/packages/esign/src/index.tsx @@ -209,8 +209,8 @@ const SuperDocESign = forwardRef { // Guard callback execution if cleanup already ran @@ -237,8 +237,8 @@ const SuperDocESign = forwardRef { if (!document.validation?.scroll?.required || !isReady) return; diff --git a/packages/esign/src/types.ts b/packages/esign/src/types.ts index 9c92117b7b..6cb252af07 100644 --- a/packages/esign/src/types.ts +++ b/packages/esign/src/types.ts @@ -72,7 +72,7 @@ export interface DocumentConfig { }; }; /** - * Document view options (recommended for v1.x): + * Document view options (recommended for SuperDoc > v1.6.x): * - 'print': Fixed page width, displays document as it prints (default) * - 'web': Content reflows to fit container width (mobile/accessibility) */