feat: add new field overflow methods (#2715)

This commit is contained in:
David Nguyen
2026-05-08 15:14:27 +10:00
committed by GitHub
parent 4877d1964a
commit 207135d6f3
28 changed files with 2437 additions and 65 deletions
+260
View File
@@ -0,0 +1,260 @@
// scripts/generate-overflow-test-pdf.mjs
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { chromium } from 'playwright';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// --- Helper functions to generate HTML for each page ---
function box(left, top, width, height) {
return `left:${left}%;top:${top}%;width:${width}%;height:${height}%;`;
}
function labelStyle(left, top) {
return `left:${left}%;top:${top}%;`;
}
function makeBox(left, top, width, height, label, { labelBelow = false } = {}) {
const labelTop = labelBelow ? top + height + 0.5 : top - 1.5;
return `
<div class="label" style="${labelStyle(left, labelTop)}">${label}</div>
<div class="box" style="${box(left, top, width, height)}"></div>`;
}
// --- Pages 1-2: Date / Email (3×3 multi-line grid with text align variants) ---
function makeFieldOverflowPageWithGrid(title) {
const startX = 10;
const boxWidth = 35;
// Section A: Single-line height
const slHeight = 1.75;
const slStartY = 15;
const slRowSpacing = 8;
const sectionARows = [
{ y: slStartY, label: 'M_AUTO TA_LEFT VA_MIDDLE' },
{ y: slStartY + slRowSpacing, label: 'M_AUTO TA_LEFT VA_MIDDLE' },
{ y: slStartY + 2 * slRowSpacing, label: 'M_AUTO TA_LEFT VA_MIDDLE' },
];
// Section B: Multi-line height — 3×3 grid
// Rows: TA_LEFT, TA_CENTER, TA_RIGHT
// Columns: short, medium, long text
const mlHeight = 12;
const mlBoxWidth = 30;
const mlColumnX = [2.5, 35, 67.5];
const mlRowY = [45, 63, 83];
const mlTextAligns = ['LEFT', 'CENTER', 'RIGHT'];
let content = `
<div class="title" style="top:3%;left:10%;">${title}</div>`;
for (const r of sectionARows) {
content += makeBox(startX, r.y, boxWidth, slHeight, r.label);
}
for (let ri = 0; ri < 3; ri++) {
for (let ci = 0; ci < 3; ci++) {
const label = `M_AUTO TA_${mlTextAligns[ri]} VA_MIDDLE`;
content += makeBox(mlColumnX[ci], mlRowY[ri], mlBoxWidth, mlHeight, label);
}
}
return content;
}
// --- Page 3: Signature (stacked multi-line, no text align control) ---
function makeSignatureOverflowPage(title) {
const startX = 10;
const boxWidth = 35;
// Section A: Single-line height
const slHeight = 1.75;
const slStartY = 15;
const slRowSpacing = 8;
const sectionARows = [
{ y: slStartY, label: 'M_AUTO TA_CENTER VA_MIDDLE' },
{ y: slStartY + slRowSpacing, label: 'M_AUTO TA_CENTER VA_MIDDLE' },
{ y: slStartY + 2 * slRowSpacing, label: 'M_AUTO TA_CENTER VA_MIDDLE' },
];
// Section B: Multi-line height — stacked single column
const mlHeight = 12;
const mlStartY = 45;
const mlRowSpacing = 18;
const sectionBRows = [
{ y: mlStartY, label: 'M_AUTO TA_CENTER VA_MIDDLE' },
{ y: mlStartY + mlRowSpacing, label: 'M_AUTO TA_CENTER VA_MIDDLE' },
{ y: mlStartY + 2 * mlRowSpacing, label: 'M_AUTO TA_CENTER VA_MIDDLE' },
];
let content = `
<div class="title" style="top:3%;left:10%;">${title}</div>`;
for (const r of sectionARows) {
content += makeBox(startX, r.y, boxWidth, slHeight, r.label);
}
for (const r of sectionBRows) {
content += makeBox(startX, r.y, boxWidth, mlHeight, r.label);
}
return content;
}
// --- Pages 4-5: Text Field Auto Mode ---
function makeTextAutoPage(title, boxHeight, isSingleLine) {
const boxWidth = 28;
const columns = [
{ col: 0, x: 5, textAlign: 'left' },
{ col: 1, x: 35.5, textAlign: 'center' },
{ col: 2, x: 66, textAlign: 'right' },
];
const verticalAligns = ['top', 'middle', 'bottom'];
let content = `
<div class="title" style="top:3%;left:5%;">${title}</div>`;
if (isSingleLine) {
// Single-line: stagger all 9 items evenly down the page.
// Each item gets its own Y position to avoid horizontal overflow collision.
const itemCount = 9; // 3 rows × 3 columns
const startY = 10;
const endY = 92;
const spacing = (endY - startY) / (itemCount - 1);
let itemIndex = 0;
for (let ri = 0; ri < 3; ri++) {
for (let ci = 0; ci < columns.length; ci++) {
const col = columns[ci];
const y = startY + itemIndex * spacing;
const label = `M_AUTO TA_${col.textAlign.toUpperCase()} VA_${verticalAligns[ri].toUpperCase()}`;
content += makeBox(col.x, y, boxWidth, boxHeight, label);
itemIndex++;
}
}
} else {
// Multi-line: 3 rows evenly spaced, bottom row near page bottom.
// Box is 12% tall. Top of last box at 80% so bottom edge is at 92%.
const startY = 10;
const lastRowY = 80; // 80 + 12 = 92% (page bottom with padding)
const midY = (startY + lastRowY) / 2; // 45%
const rowYPositions = [startY, midY, lastRowY];
for (let ri = 0; ri < 3; ri++) {
const labelBelow = verticalAligns[ri] === 'bottom';
for (const col of columns) {
const label = `M_AUTO TA_${col.textAlign.toUpperCase()} VA_${verticalAligns[ri].toUpperCase()}`;
content += makeBox(col.x, rowYPositions[ri], boxWidth, boxHeight, label, { labelBelow });
}
}
}
return content;
}
// --- Page 6: Explicit Modes ---
function makePage6() {
const boxWidth = 25;
let content = `
<div class="title" style="top:3%;left:5%;">Text Field Explicit Modes</div>
`;
// Section A: Horizontal mode — centered on page, staggered vertically
const centeredX = (100 - boxWidth) / 2; // 37.5%
const horizontalRows = [
{ x: centeredX, y: 15, label: 'M_HORIZONTAL TA_LEFT VA_TOP' },
{ x: centeredX, y: 21, label: 'M_HORIZONTAL TA_CENTER VA_TOP' },
{ x: centeredX, y: 27, label: 'M_HORIZONTAL TA_RIGHT VA_TOP' },
];
for (const r of horizontalRows) {
content += makeBox(r.x, r.y, boxWidth, 1.75, r.label);
}
// Section B: Vertical mode — place side by side so vertical overflow has room below
content += '';
const verticalCols = [
{ x: 5, y: 43, label: 'M_VERTICAL TA_LEFT VA_TOP', labelBelow: false },
{ x: 37.5, y: 43, label: 'M_VERTICAL TA_LEFT VA_MIDDLE', labelBelow: false },
{ x: 70, y: 43, label: 'M_VERTICAL TA_LEFT VA_BOTTOM', labelBelow: true },
];
for (const c of verticalCols) {
content += makeBox(c.x, c.y, boxWidth, 12, c.label, { labelBelow: c.labelBelow });
}
return content;
}
// --- Page 7: Crop Mode ---
function makePage7() {
return `
<div class="title" style="top:3%;left:10%;">Crop Mode (no overflow)</div>
${makeBox(10, 15, 25, 1.75, 'M_CROP TA_LEFT VA_TOP')}
${makeBox(10, 30, 25, 12, 'M_CROP TA_LEFT VA_TOP')}`;
}
// --- Assemble all pages ---
function buildPage(contentFn) {
return `<div class="page">${typeof contentFn === 'string' ? contentFn : contentFn}</div>`;
}
const html = `<!DOCTYPE html>
<html>
<head>
<style>
@page { size: A4; margin: 0; }
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: sans-serif; font-size: 10px; }
.page { width: 210mm; height: 297mm; position: relative; page-break-after: always; }
.page:last-child { page-break-after: auto; }
.title { position: absolute; font-size: 18px; font-weight: bold; }
.section-label { position: absolute; font-size: 12px; font-weight: bold; color: #666; }
.box { position: absolute; border: 1px solid black; box-sizing: border-box; }
.label { position: absolute; font-size: 9px; color: red; font-weight: bold; z-index: 9999; }
</style>
</head>
<body>
${buildPage(makeFieldOverflowPageWithGrid('Date Field Overflow Tests'))}
${buildPage(makeFieldOverflowPageWithGrid('Email Field Overflow Tests'))}
${buildPage(makeSignatureOverflowPage('Signature Field Overflow Tests'))}
${buildPage(makeTextAutoPage('Text Field Auto - Single-line Height', 1.75, true))}
${buildPage(makeTextAutoPage('Text Field Auto - Multi-line Height', 12, false))}
${buildPage(makeTextAutoPage('Text Field Auto - Multi-line Height Overflow', 12, false))}
${buildPage(makePage6())}
${buildPage(makePage7())}
</body>
</html>`;
async function main() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle' });
const outputPath = path.join(__dirname, '../assets/field-overflow.pdf');
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
margin: { top: '0', right: '0', bottom: '0', left: '0' },
});
await browser.close();
console.log(`PDF generated: assets/field-overflow.pdf`);
}
main().catch(console.error);