diff --git a/packages/src/components/editable/editable.component.spec.ts b/packages/src/components/editable/editable.component.spec.ts index 99f1950..5cd9bae 100644 --- a/packages/src/components/editable/editable.component.spec.ts +++ b/packages/src/components/editable/editable.component.spec.ts @@ -181,4 +181,32 @@ describe('Editable Component', () => { expect(component.scrollSelectionIntoView).toHaveBeenCalledTimes(1); })); + + it('should not steal focus from an external control while native selection sync is pending', fakeAsync(() => { + fixture.detectChanges(); + flush(); + fixture.detectChanges(); + + const editor = component.editor; + const editorElement = AngularEditor.toDOMNode(editor, editor) as HTMLElement; + const externalInput = document.createElement('input'); + document.body.appendChild(externalInput); + + try { + editorElement.focus(); + Transforms.select(editor, Editor.start(editor, [0])); + flush(); + + const end = Editor.end(editor, [0]); + editor.selection = { anchor: end, focus: end }; + component.editableComponent.toNativeSelection(false); + + externalInput.focus(); + flush(); + + expect(document.activeElement).toBe(externalInput); + } finally { + externalInput.remove(); + } + })); }); diff --git a/packages/src/components/editable/editable.component.ts b/packages/src/components/editable/editable.component.ts index d1c067c..54b9575 100644 --- a/packages/src/components/editable/editable.component.ts +++ b/packages/src/components/editable/editable.component.ts @@ -1039,7 +1039,12 @@ export class SlateEditable implements OnInit, OnChanges, OnDestroy, AfterViewChe newDomRange && autoScroll && this.scrollSelectionIntoView(this.editor, newDomRange); // COMPAT: In Firefox, it's not enough to create a range, you also need // to focus the contenteditable element too. (2016/11/16) - if (newDomRange && IS_FIREFOX) { + // Don't steal focus if another control was focused while this callback was queued. + const currentActiveElement = root.activeElement; + const documentBody = (root as Document).body; + const hasAnotherFocusedElement = + !!currentActiveElement && currentActiveElement !== el && currentActiveElement !== documentBody; + if (newDomRange && IS_FIREFOX && !hasAnotherFocusedElement) { el.focus(); } }