Skip to content

Commit 84b450c

Browse files
authored
fix: selection rectangles in headers/footers (#2852)
1 parent f866ee0 commit 84b450c

3 files changed

Lines changed: 99 additions & 0 deletions

File tree

packages/super-editor/src/editors/v1/core/presentation-editor/dom/EditorStyleInjector.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ describe('ensureEditorNativeSelectionStyles', () => {
4343
expect(css).toContain('.superdoc-layout *::selection');
4444
expect(css).toContain('.superdoc-layout *::-moz-selection');
4545
expect(css).toContain('background: transparent');
46+
expect(css).toContain('.superdoc-layout .superdoc-header-editor-host *::selection');
47+
expect(css).toContain('.superdoc-layout .superdoc-footer-editor-host *::selection');
48+
expect(css).toContain('color: HighlightText');
4649
});
4750
});
4851

packages/super-editor/src/editors/v1/core/presentation-editor/dom/EditorStyleInjector.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ const NATIVE_SELECTION_STYLES = `
3030
.superdoc-layout *::-moz-selection {
3131
background: transparent;
3232
}
33+
34+
/* Keep native selection visible inside live header/footer editors.
35+
* Unlike the main document surface, header/footer editing uses a visible
36+
* ProseMirror host. If we suppress native selection there, users can end up
37+
* with no obvious selection feedback when the custom overlay is subtle or
38+
* still syncing to the current drag gesture. */
39+
.superdoc-layout .superdoc-header-editor-host *::selection,
40+
.superdoc-layout .superdoc-footer-editor-host *::selection {
41+
background: Highlight;
42+
color: HighlightText;
43+
}
44+
45+
.superdoc-layout .superdoc-header-editor-host *::-moz-selection,
46+
.superdoc-layout .superdoc-footer-editor-host *::-moz-selection {
47+
background: Highlight;
48+
color: HighlightText;
49+
}
3350
`;
3451

3552
let nativeSelectionStylesInjected = false;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
import type { Locator, Page } from '@playwright/test';
5+
import { expect, test } from '../../fixtures/superdoc.js';
6+
7+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
8+
const DOC_PATH = path.resolve(__dirname, '../../test-data/pagination/longer-header.docx');
9+
const MOD_KEY = process.platform === 'darwin' ? 'Meta' : 'Control';
10+
11+
test.use({ config: { showSelection: true } });
12+
13+
test.skip(!fs.existsSync(DOC_PATH), 'Test document not available — run pnpm corpus:pull');
14+
15+
async function enterHeaderFooterEditMode(
16+
page: Page,
17+
surfaceSelector: string,
18+
editorHostSelector: string,
19+
): Promise<Locator> {
20+
const surface = page.locator(surfaceSelector).first();
21+
await surface.scrollIntoViewIfNeeded();
22+
await surface.waitFor({ state: 'visible', timeout: 15_000 });
23+
24+
const box = await surface.boundingBox();
25+
expect(box).toBeTruthy();
26+
await page.mouse.dblclick(box!.x + box!.width / 2, box!.y + box!.height / 2);
27+
28+
const editorHost = page.locator(editorHostSelector).first();
29+
await editorHost.waitFor({ state: 'visible', timeout: 10_000 });
30+
31+
const pm = editorHost.locator('.ProseMirror');
32+
await pm.click();
33+
34+
return pm;
35+
}
36+
37+
async function assertSelectionOverlayRenders(
38+
page: Page,
39+
editor: Locator,
40+
expectedSelectionText: string,
41+
): Promise<void> {
42+
await editor.click();
43+
await page.keyboard.press(`${MOD_KEY}+A`);
44+
45+
await expect
46+
.poll(async () => page.evaluate(() => document.getSelection()?.toString().trim() ?? ''))
47+
.toBe(expectedSelectionText);
48+
49+
await expect.poll(async () => page.locator('.presentation-editor__selection-rect').count()).toBeGreaterThan(0);
50+
51+
const selectionRect = page.locator('.presentation-editor__selection-rect');
52+
await expect(selectionRect.first()).toBeVisible();
53+
}
54+
55+
test('layout engine renders selection rectangles while editing a header', async ({ superdoc }) => {
56+
await superdoc.loadDocument(DOC_PATH);
57+
await superdoc.waitForStable();
58+
59+
const editor = await enterHeaderFooterEditMode(
60+
superdoc.page,
61+
'.superdoc-page-header',
62+
'.superdoc-header-editor-host',
63+
);
64+
65+
await assertSelectionOverlayRenders(superdoc.page, editor, 'Generic content header');
66+
});
67+
68+
test('layout engine renders selection rectangles while editing a footer', async ({ superdoc }) => {
69+
await superdoc.loadDocument(DOC_PATH);
70+
await superdoc.waitForStable();
71+
72+
const editor = await enterHeaderFooterEditMode(
73+
superdoc.page,
74+
'.superdoc-page-footer',
75+
'.superdoc-footer-editor-host',
76+
);
77+
78+
await assertSelectionOverlayRenders(superdoc.page, editor, 'Footer');
79+
});

0 commit comments

Comments
 (0)