diff --git a/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts b/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts index 7a93018b02..6e875256b9 100644 --- a/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts +++ b/packages/super-editor/src/core/presentation-editor/PresentationEditor.ts @@ -3662,7 +3662,13 @@ export class PresentationEditor extends EventEmitter { // Keep selection visible when context menu (SlashMenu) is open const slashMenuOpen = activeEditor?.state ? !!SlashMenuPluginKey.getState(activeEditor.state)?.open : false; - if (!hasFocus && !slashMenuOpen) { + // Keep selection visible when focus is on editor UI surfaces (toolbar, dropdowns). + // Naive-UI portals dropdown content under .v-binder-follower-content at level, + // so it won't be inside [data-editor-ui-surface]. Check both. + const activeEl = document.activeElement; + const isOnEditorUi = !!(activeEl as Element)?.closest?.('[data-editor-ui-surface], .v-binder-follower-content'); + + if (!hasFocus && !slashMenuOpen && !isOnEditorUi) { try { this.#clearSelectedFieldAnnotationClass(); this.#localSelectionLayer.innerHTML = ''; diff --git a/tests/visual/tests/behavior/basic-commands/sd-1905-selection-highlight-toolbar-dropdown.spec.ts b/tests/visual/tests/behavior/basic-commands/sd-1905-selection-highlight-toolbar-dropdown.spec.ts new file mode 100644 index 0000000000..0ec6db338f --- /dev/null +++ b/tests/visual/tests/behavior/basic-commands/sd-1905-selection-highlight-toolbar-dropdown.spec.ts @@ -0,0 +1,48 @@ +import { test, expect } from '../../fixtures/superdoc.js'; + +test.use({ config: { hideSelection: false } }); + +test('@behavior SD-1905 selection highlight preserved when focus moves to editor UI surface', async ({ superdoc }) => { + await superdoc.type('Select this text then open dropdown'); + await superdoc.waitForStable(); + + // Select all text via keyboard shortcut + await superdoc.selectAll(); + await superdoc.waitForStable(); + + // Verify selection overlay is rendered + const overlayChildCount = await superdoc.page.evaluate(() => { + const overlay = document.querySelector('.presentation-editor__selection-layer--local'); + return overlay ? overlay.children.length : -1; + }); + expect(overlayChildCount).toBeGreaterThan(0); + + await superdoc.screenshot('sd-1905-selection-before-ui-focus'); + + // Simulate focus moving to an editor UI surface (e.g. toolbar dropdown). + // This is what happens when a user clicks a toolbar dropdown — focus leaves + // the ProseMirror editor and moves to a UI element marked as editor UI. + await superdoc.page.evaluate(() => { + const btn = document.createElement('button'); + btn.setAttribute('data-editor-ui-surface', ''); + btn.textContent = 'Fake toolbar button'; + btn.id = 'sd-1905-test-ui-surface'; + document.body.appendChild(btn); + btn.focus(); + }); + await superdoc.waitForStable(); + + // Selection overlay should still be visible after focus moved to UI surface + const overlayAfterUiFocus = await superdoc.page.evaluate(() => { + const overlay = document.querySelector('.presentation-editor__selection-layer--local'); + return overlay ? overlay.children.length : -1; + }); + expect(overlayAfterUiFocus).toBeGreaterThan(0); + + await superdoc.screenshot('sd-1905-selection-with-ui-surface-focused'); + + // Clean up test element + await superdoc.page.evaluate(() => { + document.getElementById('sd-1905-test-ui-surface')?.remove(); + }); +});