From 94177750b6a01875a4a8f1f98bf3f0cd63cff817 Mon Sep 17 00:00:00 2001 From: Liang Meng Date: Thu, 9 Jul 2026 21:43:22 +0800 Subject: [PATCH] Fix: keep image selection when selectionchange range is collapsed --- .../corePlugin/selection/SelectionPlugin.ts | 4 +-- .../selection/SelectionPluginTest.ts | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts b/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts index d25849d57613..1576c3013757 100644 --- a/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts +++ b/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts @@ -738,11 +738,11 @@ class SelectionPlugin implements PluginWithState { const newSelection = this.editor.getDOMSelection(); const domHelper = this.editor.getDOMHelper(); - //If am image selection changed to a wider range due a keyboard event, we should update the selection + // If an image selection changed to a wider range due to a keyboard event, we should update the selection const range = domHelper.getSelectionRange(); if (range) { const image = isSingleImageInSelection(range); - if (newSelection?.type == 'image' && !image) { + if (newSelection?.type == 'image' && !image && !range.collapsed) { const sel = this.editor.getDocument().defaultView?.getSelection(); const isReverted = sel ? sel.focusNode != range.endContainer || sel.focusOffset != range.endOffset diff --git a/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts b/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts index 42357b2f8e2f..9075f41c6d37 100644 --- a/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts +++ b/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts @@ -3698,6 +3698,38 @@ describe('SelectionPlugin selectionChange on image selected', () => { }); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); }); + + it('onSelectionChange on image | 5, keeps image selection when range is collapsed', () => { + spyOn(isSingleImageInSelection, 'isSingleImageInSelection').and.returnValue(null); + + mockedRange = { startContainer: {}, collapsed: true } as any; + + const plugin = createSelectionPlugin({}); + const state = plugin.getState(); + const mockedOldSelection = { + type: 'image', + image: {} as any, + } as DOMSelection; + + state.selection = mockedOldSelection; + + plugin.initialize(editor); + + const onSelectionChange = addEventListenerSpy.calls.argsFor(0)[1] as Function; + const mockedNewSelection = { + type: 'image', + image: {} as any, + } as any; + + hasFocusSpy.and.returnValue(true); + isInShadowEditSpy.and.returnValue(false); + getDOMSelectionSpy.and.returnValue(mockedNewSelection); + + onSelectionChange(); + + expect(setDOMSelectionSpy).not.toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); + }); }); describe('SelectionPlugin handle logical root change', () => {