Skip to content

Commit a33568e

Browse files
authored
fix: preserve selection highlight when opening toolbar dropdowns (#2097)
SD-1905: The selection overlay was cleared whenever the editor lost focus, including when focus moved to toolbar dropdowns. Add an isOnEditorUi check so the overlay is preserved when the active element is inside [data-editor-ui-surface] or a Naive-UI portal (.v-binder-follower-content).
1 parent 5a41470 commit a33568e

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

packages/super-editor/src/core/presentation-editor/PresentationEditor.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3662,7 +3662,13 @@ export class PresentationEditor extends EventEmitter {
36623662
// Keep selection visible when context menu (SlashMenu) is open
36633663
const slashMenuOpen = activeEditor?.state ? !!SlashMenuPluginKey.getState(activeEditor.state)?.open : false;
36643664

3665-
if (!hasFocus && !slashMenuOpen) {
3665+
// Keep selection visible when focus is on editor UI surfaces (toolbar, dropdowns).
3666+
// Naive-UI portals dropdown content under .v-binder-follower-content at <body> level,
3667+
// so it won't be inside [data-editor-ui-surface]. Check both.
3668+
const activeEl = document.activeElement;
3669+
const isOnEditorUi = !!(activeEl as Element)?.closest?.('[data-editor-ui-surface], .v-binder-follower-content');
3670+
3671+
if (!hasFocus && !slashMenuOpen && !isOnEditorUi) {
36663672
try {
36673673
this.#clearSelectedFieldAnnotationClass();
36683674
this.#localSelectionLayer.innerHTML = '';
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { test, expect } from '../../fixtures/superdoc.js';
2+
3+
test.use({ config: { hideSelection: false } });
4+
5+
test('@behavior SD-1905 selection highlight preserved when focus moves to editor UI surface', async ({ superdoc }) => {
6+
await superdoc.type('Select this text then open dropdown');
7+
await superdoc.waitForStable();
8+
9+
// Select all text via keyboard shortcut
10+
await superdoc.selectAll();
11+
await superdoc.waitForStable();
12+
13+
// Verify selection overlay is rendered
14+
const overlayChildCount = await superdoc.page.evaluate(() => {
15+
const overlay = document.querySelector('.presentation-editor__selection-layer--local');
16+
return overlay ? overlay.children.length : -1;
17+
});
18+
expect(overlayChildCount).toBeGreaterThan(0);
19+
20+
await superdoc.screenshot('sd-1905-selection-before-ui-focus');
21+
22+
// Simulate focus moving to an editor UI surface (e.g. toolbar dropdown).
23+
// This is what happens when a user clicks a toolbar dropdown — focus leaves
24+
// the ProseMirror editor and moves to a UI element marked as editor UI.
25+
await superdoc.page.evaluate(() => {
26+
const btn = document.createElement('button');
27+
btn.setAttribute('data-editor-ui-surface', '');
28+
btn.textContent = 'Fake toolbar button';
29+
btn.id = 'sd-1905-test-ui-surface';
30+
document.body.appendChild(btn);
31+
btn.focus();
32+
});
33+
await superdoc.waitForStable();
34+
35+
// Selection overlay should still be visible after focus moved to UI surface
36+
const overlayAfterUiFocus = await superdoc.page.evaluate(() => {
37+
const overlay = document.querySelector('.presentation-editor__selection-layer--local');
38+
return overlay ? overlay.children.length : -1;
39+
});
40+
expect(overlayAfterUiFocus).toBeGreaterThan(0);
41+
42+
await superdoc.screenshot('sd-1905-selection-with-ui-surface-focused');
43+
44+
// Clean up test element
45+
await superdoc.page.evaluate(() => {
46+
document.getElementById('sd-1905-test-ui-surface')?.remove();
47+
});
48+
});

0 commit comments

Comments
 (0)