Skip to content

Commit 312e75d

Browse files
fix: change default line height for header/footer editor (#2672)
* fix: change default line height for header/footer editor * test: add behavior tests for header/footer line height (SD-2235) Verify that header/footer editors use lineHeight 1 (matching OOXML Header style w:line="240"), body editors retain the default 1.2, and header content is not clipped when entering edit mode. * fix: add constant for header/footer line-height --------- Co-authored-by: Caio Pizzol <caio@harbourshare.com>
1 parent 512d4da commit 312e75d

2 files changed

Lines changed: 104 additions & 1 deletion

File tree

packages/super-editor/src/editors/v1/core/renderers/ProseMirrorRenderer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const RESIZE_DEBOUNCE_MS = 150;
4242
* This value provides consistent vertical spacing and improves readability.
4343
*/
4444
const DEFAULT_LINE_HEIGHT = 1.2;
45+
const HEADER_FOOTER_LINE_HEIGHT = 1;
4546

4647
/**
4748
* Listener cleanup function type for tracking registered event listeners.
@@ -617,7 +618,9 @@ export class ProseMirrorRenderer implements EditorRenderer {
617618
}
618619

619620
// Line height
620-
proseMirror.style.lineHeight = String(DEFAULT_LINE_HEIGHT);
621+
proseMirror.style.lineHeight = editor.options.isHeaderOrFooter
622+
? String(HEADER_FOOTER_LINE_HEIGHT)
623+
: String(DEFAULT_LINE_HEIGHT);
621624

622625
// Mobile styles
623626
element.style.transformOrigin = 'top left';
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
import { test, expect } from '../../fixtures/superdoc.js';
5+
6+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
7+
const DOC_PATH = path.resolve(__dirname, '../../test-data/pagination/longer-header.docx');
8+
9+
test.skip(!fs.existsSync(DOC_PATH), 'Test document not available — run pnpm corpus:pull');
10+
11+
test('header editor uses line-height 1, not the default 1.2', async ({ superdoc }) => {
12+
await superdoc.loadDocument(DOC_PATH);
13+
await superdoc.waitForStable();
14+
15+
const header = superdoc.page.locator('.superdoc-page-header').first();
16+
await header.waitFor({ state: 'visible', timeout: 15_000 });
17+
18+
// Double-click to enter header edit mode
19+
const box = await header.boundingBox();
20+
expect(box).toBeTruthy();
21+
await superdoc.page.mouse.dblclick(box!.x + box!.width / 2, box!.y + box!.height / 2);
22+
await superdoc.waitForStable();
23+
24+
const editorHost = superdoc.page.locator('.superdoc-header-editor-host').first();
25+
await editorHost.waitFor({ state: 'visible', timeout: 10_000 });
26+
27+
// The ProseMirror element inside the header editor should have lineHeight: 1
28+
// (matching OOXML Header style w:line="240" w:lineRule="auto" = 240/240 = 1.0)
29+
const pm = editorHost.locator('.ProseMirror');
30+
await expect(pm).toHaveCSS('line-height', /^\d+(\.\d+)?px$/);
31+
32+
const lineHeight = await pm.evaluate((el) => el.style.lineHeight);
33+
expect(lineHeight).toBe('1');
34+
});
35+
36+
test('footer editor uses line-height 1, not the default 1.2', async ({ superdoc }) => {
37+
await superdoc.loadDocument(DOC_PATH);
38+
await superdoc.waitForStable();
39+
40+
const footer = superdoc.page.locator('.superdoc-page-footer').first();
41+
await footer.scrollIntoViewIfNeeded();
42+
await footer.waitFor({ state: 'visible', timeout: 15_000 });
43+
44+
// Double-click to enter footer edit mode
45+
const box = await footer.boundingBox();
46+
expect(box).toBeTruthy();
47+
await superdoc.page.mouse.dblclick(box!.x + box!.width / 2, box!.y + box!.height / 2);
48+
await superdoc.waitForStable();
49+
50+
const editorHost = superdoc.page.locator('.superdoc-footer-editor-host').first();
51+
await editorHost.waitFor({ state: 'visible', timeout: 10_000 });
52+
53+
const pm = editorHost.locator('.ProseMirror');
54+
const lineHeight = await pm.evaluate((el) => el.style.lineHeight);
55+
expect(lineHeight).toBe('1');
56+
});
57+
58+
test('body editor still uses default line-height 1.2', async ({ superdoc }) => {
59+
await superdoc.loadDocument(DOC_PATH);
60+
await superdoc.waitForStable();
61+
62+
// The body editor's ProseMirror should retain the default 1.2 line height
63+
const lineHeight = await superdoc.page.evaluate(() => {
64+
const editor = (window as any).editor;
65+
const pm = editor?.view?.dom as HTMLElement | undefined;
66+
return pm?.style.lineHeight;
67+
});
68+
expect(lineHeight).toBe('1.2');
69+
});
70+
71+
test('header content is not clipped when entering edit mode', async ({ superdoc }) => {
72+
await superdoc.loadDocument(DOC_PATH);
73+
await superdoc.waitForStable();
74+
75+
const header = superdoc.page.locator('.superdoc-page-header').first();
76+
await header.waitFor({ state: 'visible', timeout: 15_000 });
77+
78+
// Double-click to enter header edit mode
79+
const box = await header.boundingBox();
80+
expect(box).toBeTruthy();
81+
await superdoc.page.mouse.dblclick(box!.x + box!.width / 2, box!.y + box!.height / 2);
82+
await superdoc.waitForStable();
83+
84+
const editorHost = superdoc.page.locator('.superdoc-header-editor-host').first();
85+
await editorHost.waitFor({ state: 'visible', timeout: 10_000 });
86+
87+
// The ProseMirror content should not overflow the editor host container
88+
const overflow = await editorHost.evaluate((host) => {
89+
const pm = host.querySelector('.ProseMirror') as HTMLElement;
90+
if (!pm) return { error: 'no PM' };
91+
return {
92+
pmScrollHeight: pm.scrollHeight,
93+
pmOffsetHeight: pm.offsetHeight,
94+
hostHeight: host.offsetHeight,
95+
isOverflowing: pm.scrollHeight > host.offsetHeight,
96+
};
97+
});
98+
expect(overflow).not.toHaveProperty('error');
99+
expect(overflow.isOverflowing).toBe(false);
100+
});

0 commit comments

Comments
 (0)