From 5e32a986cec5bc9330546c5631b8e9dfce9d8cb1 Mon Sep 17 00:00:00 2001
From: ghiscoding <gbeaulac@gmail.com>
Date: Thu, 23 Feb 2023 17:10:50 -0500
Subject: [PATCH] fix: Edt cell mouseout should save & excel copy buffer should
 still work - fixes a regression caused by previous PR #901 and identified in
 Angular-Slickgrid issue
 [1103](https://github.com/ghiscoding/Angular-Slickgrid/issues/1103), the
 previous PR #901 was put in place to fix cell external copy in a bug reported
 in Slickgrid-React issue
 [36](https://github.com/ghiscoding/slickgrid-react/issues/36) - this bug
 actually came from a very old patch that was put in place via
 `suppressActiveCellChangeOnEdit: true` in SlickGrid [PR
 243](https://github.com/6pac/SlickGrid/pull/243) and this flag was to avoid
 trigger an event when the active cell changes, this event was being listened
 by SlickCellSelectionModel and when triggered was then sending another event
 with the cell ranges that changed, which then sent another event
 onSelectedRangesChanged which was itself listened by
 SlickCellExternalCopyManager and when triggered was calling a `grid.focus()`
 and when it did that, it was making the editor loses its focus (hence the
 implementation of `suppressActiveCellChangeOnEdit`) which is required by
 SlickCellExternalCopyManager to be able to copy & paste cell ranges. After
 investigating, I was able to remove the use of
 `suppressActiveCellChangeOnEdit` (now disabled globally) by simply calling
 `grid.focus()` in each editor prior to itself getting its own focus, so at
 least our focus remains in the grid and our editor no longer loses its focus
 and we are also able to finally use Editor + CellExternalCopyManager at the
 same time without conflict anymore - hopefully this fixes all regressions and
 it also removes some old hacks (suppressActiveCellChangeOnEdit) that was put
 in place, in summary, it should be a lot cleaner now

---
 package.json                                  |  2 +-
 .../__tests__/autocompleterEditor.spec.ts     |  2 ++
 .../editors/__tests__/checkboxEditor.spec.ts  |  2 ++
 .../src/editors/__tests__/dateEditor.spec.ts  |  2 ++
 .../editors/__tests__/dualInputEditor.spec.ts |  2 ++
 .../src/editors/__tests__/floatEditor.spec.ts |  4 ++++
 .../src/editors/__tests__/inputEditor.spec.ts |  4 ++++
 .../__tests__/inputPasswordEditor.spec.ts     |  6 +++++
 .../editors/__tests__/integerEditor.spec.ts   |  7 ++++++
 .../editors/__tests__/longTextEditor.spec.ts  |  6 +++++
 .../__tests__/multipleSelectEditor.spec.ts    |  1 +
 .../editors/__tests__/selectEditor.spec.ts    |  2 ++
 .../__tests__/singleSelectEditor.spec.ts      |  1 +
 .../editors/__tests__/sliderEditor.spec.ts    |  3 +++
 .../common/src/editors/autocompleterEditor.ts |  3 +++
 packages/common/src/editors/checkboxEditor.ts |  3 +++
 packages/common/src/editors/dateEditor.ts     |  8 ++++---
 .../common/src/editors/dualInputEditor.ts     | 11 +++++----
 packages/common/src/editors/inputEditor.ts    |  3 +++
 packages/common/src/editors/longTextEditor.ts |  5 +++-
 packages/common/src/editors/selectEditor.ts   |  3 +++
 packages/common/src/editors/sliderEditor.ts   |  3 +++
 .../slickCellExcelCopyManager.spec.ts         |  7 ++++++
 .../slickCellExternalCopyManager.ts           |  8 +++++--
 packages/common/src/global-grid-options.ts    |  2 +-
 .../src/interfaces/gridOption.interface.ts    |  5 ++--
 .../__tests__/gridEvent.service.spec.ts       | 24 -------------------
 .../common/src/services/gridEvent.service.ts  |  8 -------
 .../src/compositeEditor.factory.spec.ts       |  1 +
 29 files changed, 91 insertions(+), 47 deletions(-)

diff --git a/package.json b/package.json
index 68b488949..a2b74bbd1 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
     "build:watch": "tsc --build ./tsconfig.packages.json --watch",
     "predev": "pnpm build:esm:styles",
     "dev": "run-p dev:watch webpack:watch",
-    "dev:watch": "lerna watch --no-bail --file-delimiter=\",\" --glob=\"src/**/*.{ts,scss}\" --ignored=\"src/**/*.spec.ts\" -- cross-env-shell pnpm run -r --filter $LERNA_PACKAGE_NAME dev",
+    "dev:watch": "lerna watch --no-bail --file-delimiter=\",\" --glob=\"src/**/*.{ts,scss}\" --ignored=\"**/*.spec.ts\" -- cross-env-shell pnpm run -r --filter $LERNA_PACKAGE_NAME dev",
     "webpack:watch": "pnpm -r --parallel run webpack:dev",
     "preview:publish": "lerna publish from-package --dry-run",
     "preview:version": "lerna version --dry-run",
diff --git a/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts b/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts
index 570076aa7..ca471198a 100644
--- a/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/autocompleterEditor.spec.ts
@@ -28,6 +28,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getOptions: () => gridOptionMock,
   getColumns: jest.fn(),
@@ -265,6 +266,7 @@ describe('AutocompleterEditor', () => {
       const spy = jest.spyOn(editorElm, 'focus');
       editor.focus();
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spy).toHaveBeenCalled();
     });
 
diff --git a/packages/common/src/editors/__tests__/checkboxEditor.spec.ts b/packages/common/src/editors/__tests__/checkboxEditor.spec.ts
index 39884d5eb..73c4e050e 100644
--- a/packages/common/src/editors/__tests__/checkboxEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/checkboxEditor.spec.ts
@@ -23,6 +23,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getOptions: () => gridOptionMock,
   getColumns: jest.fn(),
@@ -200,6 +201,7 @@ describe('CheckboxEditor', () => {
         editor.focus();
         editor.editorDomElement.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(false);
       });
     });
diff --git a/packages/common/src/editors/__tests__/dateEditor.spec.ts b/packages/common/src/editors/__tests__/dateEditor.spec.ts
index e289d3b02..0077acec8 100644
--- a/packages/common/src/editors/__tests__/dateEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/dateEditor.spec.ts
@@ -27,6 +27,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -190,6 +191,7 @@ describe('DateEditor', () => {
       editor.show();
       editor.focus();
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(calendarElm).toBeTruthy();
       expect(spy).toHaveBeenCalled();
     });
diff --git a/packages/common/src/editors/__tests__/dualInputEditor.spec.ts b/packages/common/src/editors/__tests__/dualInputEditor.spec.ts
index c0f6dae67..85acc4ec6 100644
--- a/packages/common/src/editors/__tests__/dualInputEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/dualInputEditor.spec.ts
@@ -27,6 +27,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -215,6 +216,7 @@ describe('DualInputEditor', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
     });
 
diff --git a/packages/common/src/editors/__tests__/floatEditor.spec.ts b/packages/common/src/editors/__tests__/floatEditor.spec.ts
index afe753d73..2940c0fc3 100644
--- a/packages/common/src/editors/__tests__/floatEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/floatEditor.spec.ts
@@ -27,6 +27,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -178,6 +179,7 @@ describe('FloatEditor', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
     });
 
@@ -191,6 +193,7 @@ describe('FloatEditor', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
     });
 
@@ -205,6 +208,7 @@ describe('FloatEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(true);
       });
 
diff --git a/packages/common/src/editors/__tests__/inputEditor.spec.ts b/packages/common/src/editors/__tests__/inputEditor.spec.ts
index 2e065e0c1..ca553e9f0 100644
--- a/packages/common/src/editors/__tests__/inputEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/inputEditor.spec.ts
@@ -27,6 +27,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -188,6 +189,7 @@ describe('InputEditor (TextEditor)', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
       expect(editor.isValueTouched()).toBe(true);
     });
@@ -202,6 +204,7 @@ describe('InputEditor (TextEditor)', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
     });
 
@@ -216,6 +219,7 @@ describe('InputEditor (TextEditor)', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(true);
       });
 
diff --git a/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts b/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts
index 511aa8055..1fdcdb8e8 100644
--- a/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/inputPasswordEditor.spec.ts
@@ -27,6 +27,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -188,6 +189,7 @@ describe('InputPasswordEditor', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
       expect(editor.isValueTouched()).toBe(true);
     });
@@ -202,6 +204,7 @@ describe('InputPasswordEditor', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
     });
 
@@ -216,6 +219,7 @@ describe('InputPasswordEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(true);
       });
 
@@ -229,6 +233,7 @@ describe('InputPasswordEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(false);
       });
 
@@ -242,6 +247,7 @@ describe('InputPasswordEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(true);
       });
     });
diff --git a/packages/common/src/editors/__tests__/integerEditor.spec.ts b/packages/common/src/editors/__tests__/integerEditor.spec.ts
index 94c36ab2c..4c36d0bab 100644
--- a/packages/common/src/editors/__tests__/integerEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/integerEditor.spec.ts
@@ -27,6 +27,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -180,6 +181,7 @@ describe('IntegerEditor', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
     });
 
@@ -193,6 +195,7 @@ describe('IntegerEditor', () => {
       editor.focus();
       editorElm.dispatchEvent(event);
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(spyEvent).toHaveBeenCalled();
     });
 
@@ -207,6 +210,7 @@ describe('IntegerEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(true);
       });
 
@@ -220,6 +224,7 @@ describe('IntegerEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(false);
       });
 
@@ -233,6 +238,7 @@ describe('IntegerEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(false);
       });
 
@@ -246,6 +252,7 @@ describe('IntegerEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(true);
       });
     });
diff --git a/packages/common/src/editors/__tests__/longTextEditor.spec.ts b/packages/common/src/editors/__tests__/longTextEditor.spec.ts
index dc7bd43cb..5674e31d5 100644
--- a/packages/common/src/editors/__tests__/longTextEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/longTextEditor.spec.ts
@@ -31,6 +31,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -253,6 +254,7 @@ describe('LongTextEditor', () => {
         editorElm.dispatchEvent(eventKeyDown);
         editorElm.dispatchEvent(eventInput);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(currentTextLengthElm.textContent).toBe('1');
         expect(maxTextLengthElm.textContent).toBe('255');
         expect(editor.isValueChanged()).toBe(true);
@@ -269,6 +271,7 @@ describe('LongTextEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(false);
         expect(editor.isValueTouched()).toBe(true);
       });
@@ -283,6 +286,7 @@ describe('LongTextEditor', () => {
         editor.focus();
         editorElm.dispatchEvent(event);
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editor.isValueChanged()).toBe(true);
         expect(editor.isValueTouched()).toBe(true);
       });
@@ -742,6 +746,7 @@ describe('LongTextEditor', () => {
         const currentTextLengthElm = document.body.querySelector('.editor-footer .text-length') as HTMLDivElement;
         const maxTextLengthElm = document.body.querySelector('.editor-footer .max-length') as HTMLDivElement;
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editorElm.value).toBe('some extra');
         expect(currentTextLengthElm.textContent).toBe('10');
         expect(maxTextLengthElm.textContent).toBe('10');
@@ -763,6 +768,7 @@ describe('LongTextEditor', () => {
         const currentTextLengthElm = document.body.querySelector('.editor-footer .text-length') as HTMLDivElement;
         const maxTextLengthElm = document.body.querySelector('.editor-footer .max-length') as HTMLDivElement;
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(editorElm.value).toBe('some extra');
         expect(currentTextLengthElm.textContent).toBe('10');
         expect(maxTextLengthElm.textContent).toBe('10');
diff --git a/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts b/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts
index 3a63a49c3..0be97e0cc 100644
--- a/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/multipleSelectEditor.spec.ts
@@ -28,6 +28,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getOptions: () => gridOptionMock,
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
diff --git a/packages/common/src/editors/__tests__/selectEditor.spec.ts b/packages/common/src/editors/__tests__/selectEditor.spec.ts
index e21763d19..b8a9a4b99 100644
--- a/packages/common/src/editors/__tests__/selectEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/selectEditor.spec.ts
@@ -28,6 +28,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -146,6 +147,7 @@ describe('SelectEditor', () => {
       editor.focus();
       const editorCount = document.body.querySelectorAll('select.ms-filter.editor-gender').length;
 
+      expect(gridStub.focus).toHaveBeenCalled();
       expect(editorCount).toBe(1);
     });
 
diff --git a/packages/common/src/editors/__tests__/singleSelectEditor.spec.ts b/packages/common/src/editors/__tests__/singleSelectEditor.spec.ts
index 7945f7f45..9ab4a8176 100644
--- a/packages/common/src/editors/__tests__/singleSelectEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/singleSelectEditor.spec.ts
@@ -26,6 +26,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getOptions: () => gridOptionMock,
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
diff --git a/packages/common/src/editors/__tests__/sliderEditor.spec.ts b/packages/common/src/editors/__tests__/sliderEditor.spec.ts
index 0034f0111..11544aa95 100644
--- a/packages/common/src/editors/__tests__/sliderEditor.spec.ts
+++ b/packages/common/src/editors/__tests__/sliderEditor.spec.ts
@@ -23,6 +23,7 @@ const getEditorLockMock = {
 };
 
 const gridStub = {
+  focus: jest.fn(),
   getActiveCell: jest.fn(),
   getColumns: jest.fn(),
   getEditorLock: () => getEditorLockMock,
@@ -421,9 +422,11 @@ describe('SliderEditor', () => {
         const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');
 
         editor = new SliderEditor(editorArguments);
+        editor.focus();
         editor.loadValue(mockItemData);
         editor.save();
 
+        expect(gridStub.focus).toHaveBeenCalled();
         expect(spy).toHaveBeenCalled();
       });
 
diff --git a/packages/common/src/editors/autocompleterEditor.ts b/packages/common/src/editors/autocompleterEditor.ts
index 7ae4ec22d..a5c2c6b2b 100644
--- a/packages/common/src/editors/autocompleterEditor.ts
+++ b/packages/common/src/editors/autocompleterEditor.ts
@@ -221,6 +221,9 @@ export class AutocompleterEditor<T extends AutocompleteItem = any> implements Ed
   }
 
   focus() {
+    // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position
+    this.grid.focus();
+
     if (this._inputElm) {
       this._inputElm.focus();
       this._inputElm.select();
diff --git a/packages/common/src/editors/checkboxEditor.ts b/packages/common/src/editors/checkboxEditor.ts
index 412177116..f7590773f 100644
--- a/packages/common/src/editors/checkboxEditor.ts
+++ b/packages/common/src/editors/checkboxEditor.ts
@@ -136,6 +136,9 @@ export class CheckboxEditor implements Editor {
   }
 
   focus(): void {
+    // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position
+    this.grid.focus();
+
     if (this._input) {
       this._input.focus();
     }
diff --git a/packages/common/src/editors/dateEditor.ts b/packages/common/src/editors/dateEditor.ts
index 8eeb013cb..2e25230f4 100644
--- a/packages/common/src/editors/dateEditor.ts
+++ b/packages/common/src/editors/dateEditor.ts
@@ -240,9 +240,11 @@ export class DateEditor implements Editor {
   }
 
   focus() {
-    if (this._inputElm?.focus) {
-      this._inputElm.focus();
-    }
+    // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position
+    this.grid.focus();
+
+    this.show();
+    this._inputElm?.focus();
     if (this._inputWithDataElm?.focus) {
       this._inputWithDataElm.focus();
       this._inputWithDataElm.select();
diff --git a/packages/common/src/editors/dualInputEditor.ts b/packages/common/src/editors/dualInputEditor.ts
index f73b5b9cd..2c2ea7880 100644
--- a/packages/common/src/editors/dualInputEditor.ts
+++ b/packages/common/src/editors/dualInputEditor.ts
@@ -82,7 +82,7 @@ export class DualInputEditor implements Editor {
   }
 
   /** Getter for the Editor DOM Element */
-  get editorDomElement(): { leftInput: HTMLInputElement, rightInput: HTMLInputElement } {
+  get editorDomElement(): { leftInput: HTMLInputElement, rightInput: HTMLInputElement; } {
     return { leftInput: this._leftInput, rightInput: this._rightInput };
   }
 
@@ -232,7 +232,8 @@ export class DualInputEditor implements Editor {
   }
 
   focus() {
-    // do nothing since we have 2 inputs and we might focus on left/right depending on which is invalid and/or new
+    // always set focus on grid first, then do nothing since we have 2 inputs and we might focus on left/right depending on which is invalid and/or new
+    this.grid.focus();
   }
 
   show() {
@@ -243,7 +244,7 @@ export class DualInputEditor implements Editor {
     }
   }
 
-  getValues(): { [fieldName: string]: string | number } {
+  getValues(): { [fieldName: string]: string | number; } {
     const obj = {};
     const leftInputValue = this._leftInput.value;
     const rightInputValue = this._rightInput.value;
@@ -382,7 +383,7 @@ export class DualInputEditor implements Editor {
     }
   }
 
-  serializeValue(): { [fieldName: string]: any } {
+  serializeValue(): { [fieldName: string]: any; } {
     const obj = {};
     const leftValue = this.serializeValueByPosition('leftInput');
     const rightValue = this.serializeValueByPosition('rightInput');
@@ -435,7 +436,7 @@ export class DualInputEditor implements Editor {
     return '1';
   }
 
-  validate(_targetElm?: any, inputValidation?: { position: 'leftInput' | 'rightInput', inputValue: any }): EditorValidationResult {
+  validate(_targetElm?: any, inputValidation?: { position: 'leftInput' | 'rightInput', inputValue: any; }): EditorValidationResult {
     // when using Composite Editor, we also want to recheck if the field if disabled/enabled since it might change depending on other inputs on the composite form
     if (this.args.compositeEditorOptions) {
       this.applyInputUsabilityState();
diff --git a/packages/common/src/editors/inputEditor.ts b/packages/common/src/editors/inputEditor.ts
index d6e327555..e2bdfe944 100644
--- a/packages/common/src/editors/inputEditor.ts
+++ b/packages/common/src/editors/inputEditor.ts
@@ -147,6 +147,9 @@ export class InputEditor implements Editor {
   }
 
   focus(): void {
+    // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position
+    this.grid.focus();
+
     if (this._input) {
       this._input.focus();
     }
diff --git a/packages/common/src/editors/longTextEditor.ts b/packages/common/src/editors/longTextEditor.ts
index 58639a8bd..1d45de0bc 100644
--- a/packages/common/src/editors/longTextEditor.ts
+++ b/packages/common/src/editors/longTextEditor.ts
@@ -224,6 +224,9 @@ export class LongTextEditor implements Editor {
   }
 
   focus() {
+    // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position
+    this.grid.focus();
+
     if (this._textareaElm) {
       this._textareaElm.focus();
       this._textareaElm.select();
@@ -433,7 +436,7 @@ export class LongTextEditor implements Editor {
   }
 
   /** On every input change event, we'll update the current text length counter */
-  protected handleOnInputChange(event: Event & { clipboardData: DataTransfer, target: HTMLTextAreaElement }) {
+  protected handleOnInputChange(event: Event & { clipboardData: DataTransfer, target: HTMLTextAreaElement; }) {
     const compositeEditorOptions = this.args.compositeEditorOptions;
     const maxLength = this.columnEditor?.maxLength;
 
diff --git a/packages/common/src/editors/selectEditor.ts b/packages/common/src/editors/selectEditor.ts
index ed18f70ac..6873b949d 100644
--- a/packages/common/src/editors/selectEditor.ts
+++ b/packages/common/src/editors/selectEditor.ts
@@ -507,6 +507,9 @@ export class SelectEditor implements Editor {
   }
 
   focus() {
+    // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position
+    this.grid.focus();
+
     if (this.$editorElm && this.$editorElm.multipleSelect) {
       this.$editorElm.multipleSelect('focus');
     }
diff --git a/packages/common/src/editors/sliderEditor.ts b/packages/common/src/editors/sliderEditor.ts
index a6023bf5e..e0e5c6c3c 100644
--- a/packages/common/src/editors/sliderEditor.ts
+++ b/packages/common/src/editors/sliderEditor.ts
@@ -157,6 +157,9 @@ export class SliderEditor implements Editor {
   }
 
   focus() {
+    // always set focus on grid first so that plugin to copy range (SlickCellExternalCopyManager) would still be able to paste at that position
+    this.grid.focus();
+
     if (this._inputElm) {
       this._inputElm.focus();
     }
diff --git a/packages/common/src/extensions/__tests__/slickCellExcelCopyManager.spec.ts b/packages/common/src/extensions/__tests__/slickCellExcelCopyManager.spec.ts
index eaddf6bc6..6c78d6f29 100644
--- a/packages/common/src/extensions/__tests__/slickCellExcelCopyManager.spec.ts
+++ b/packages/common/src/extensions/__tests__/slickCellExcelCopyManager.spec.ts
@@ -8,10 +8,17 @@ import { SlickCellExternalCopyManager } from '../slickCellExternalCopyManager';
 declare const Slick: SlickNamespace;
 jest.mock('flatpickr', () => { });
 
+const getEditorLockMock = {
+  isActive: jest.fn(),
+  commitCurrentEdit: jest.fn(),
+};
+
 const gridStub = {
   getData: jest.fn(),
   getOptions: jest.fn(),
   getSelectionModel: jest.fn(),
+  getEditorLock: () => getEditorLockMock,
+  focus: jest.fn(),
   registerPlugin: jest.fn(),
   setSelectionModel: jest.fn(),
   onKeyDown: new Slick.Event(),
diff --git a/packages/common/src/extensions/slickCellExternalCopyManager.ts b/packages/common/src/extensions/slickCellExternalCopyManager.ts
index bf968e169..364a5265b 100644
--- a/packages/common/src/extensions/slickCellExternalCopyManager.ts
+++ b/packages/common/src/extensions/slickCellExternalCopyManager.ts
@@ -73,9 +73,13 @@ export class SlickCellExternalCopyManager {
       throw new Error(`Selection model is mandatory for this plugin. Please set a selection model on the grid before adding this plugin: grid.setSelectionModel(new Slick.CellSelectionModel())`);
     }
 
-    // we give focus on the grid when a selection is done on it.
+    // we give focus on the grid when a selection is done on it (unless it's an editor, if so the editor should have already set focus to the grid prior to editing a cell).
     // without this, if the user selects a range of cell without giving focus on a particular cell, the grid doesn't get the focus and key stroke handles (ctrl+c) don't work
-    this._eventHandler.subscribe(cellSelectionModel.onSelectedRangesChanged, () => this._grid.focus());
+    this._eventHandler.subscribe(cellSelectionModel.onSelectedRangesChanged, () => {
+      if (!this._grid.getEditorLock().isActive()) {
+        this._grid.focus();
+      }
+    });
   }
 
   dispose() {
diff --git a/packages/common/src/global-grid-options.ts b/packages/common/src/global-grid-options.ts
index a46befc88..ff728b584 100644
--- a/packages/common/src/global-grid-options.ts
+++ b/packages/common/src/global-grid-options.ts
@@ -235,7 +235,7 @@ export const GlobalGridOptions: GridOption = {
   numberedMultiColumnSort: true,
   tristateMultiColumnSort: false,
   sortColNumberInSeparateSpan: true,
-  suppressActiveCellChangeOnEdit: true,
+  suppressActiveCellChangeOnEdit: false,
   pagination: {
     pageSizes: [10, 15, 20, 25, 30, 40, 50, 75, 100],
     pageSize: 25,
diff --git a/packages/common/src/interfaces/gridOption.interface.ts b/packages/common/src/interfaces/gridOption.interface.ts
index 6fdfca1ee..a2d6eb5e2 100644
--- a/packages/common/src/interfaces/gridOption.interface.ts
+++ b/packages/common/src/interfaces/gridOption.interface.ts
@@ -609,8 +609,9 @@ export interface GridOption {
   sortColNumberInSeparateSpan?: boolean;
 
   /**
-   * Defaults to true, which leads to suppress the cell from becoming active when cell as an editor and is clicked.
-   * This flag should be enabled especially when mixing these 2 features (Row Selections & Inline Editors)
+   * Defaults to false, which leads to suppress the cell from becoming active when cell as an editor and is clicked.
+   * This flag was originally enabled to work properly with (Row Selections & Inline Editors) features but it caused problem when also used with CellExternalCopyManager,
+   * however this flag shouldn't be need anymore when editing & using all 3 features and the flag's default is now disabled (false) but user can still change it if needed.
    */
   suppressActiveCellChangeOnEdit?: boolean;
 
diff --git a/packages/common/src/services/__tests__/gridEvent.service.spec.ts b/packages/common/src/services/__tests__/gridEvent.service.spec.ts
index 4ee50de5b..6202e156d 100644
--- a/packages/common/src/services/__tests__/gridEvent.service.spec.ts
+++ b/packages/common/src/services/__tests__/gridEvent.service.spec.ts
@@ -178,30 +178,6 @@ describe('GridEventService', () => {
         dataContext: mockRowData
       });
     });
-
-    it('should execute the column "onCellClick" callback method and "setActiveCell" only when enableExcelCopyBuffer is enabled', () => {
-      gridStub.getOptions = jest.fn();
-      jest.spyOn(gridStub, 'getOptions').mockReturnValue({ enableCellNavigation: true, enableExcelCopyBuffer: true });
-      const spyActive = jest.spyOn(gridStub, 'setActiveCell');
-      const spyGetCols = jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn]);
-      const spyGetData = jest.spyOn(gridStub, 'getDataItem').mockReturnValue(mockRowData);
-      const spyOnChange = jest.spyOn(mockColumn, 'onCellClick');
-
-      service.bindOnClick(gridStub);
-      gridStub.onClick.notify({ cell: 0, row: 2, grid: gridStub }, new Slick.EventData(), gridStub);
-
-      expect(spyActive).toHaveBeenCalledWith(2, 0);
-      expect(spyGetCols).toHaveBeenCalled();
-      expect(spyGetData).toHaveBeenCalled();
-      expect(spyOnChange).toHaveBeenCalledWith(expect.anything(), {
-        row: 2,
-        cell: 0,
-        dataView: dataViewStub,
-        grid: gridStub,
-        columnDef: mockColumn,
-        dataContext: mockRowData
-      });
-    });
   });
 
   describe('dispose method', () => {
diff --git a/packages/common/src/services/gridEvent.service.ts b/packages/common/src/services/gridEvent.service.ts
index d063c934e..ec46f3c69 100644
--- a/packages/common/src/services/gridEvent.service.ts
+++ b/packages/common/src/services/gridEvent.service.ts
@@ -1,6 +1,5 @@
 import {
   Column,
-  GridOption,
   OnEventArgs,
   SlickDataView,
   SlickEventHandler,
@@ -93,13 +92,6 @@ export class GridEventService {
         return;
       }
       const column: Column = grid.getColumns?.()[args.cell];
-      const gridOptions: GridOption = grid.getOptions?.() || {};
-
-      // when using Excel copy buffer to copy cell ranges, the cell loses its focus after the copy execution
-      // so we need to reapply the focus on the active cell that the user clicked
-      if (gridOptions.enableCellNavigation && gridOptions.enableExcelCopyBuffer) {
-        grid.setActiveCell(args.row, args.cell);
-      }
 
       // if the column definition has a onCellClick property (a callback function), then run it
       if (typeof column.onCellClick === 'function') {
diff --git a/packages/composite-editor-component/src/compositeEditor.factory.spec.ts b/packages/composite-editor-component/src/compositeEditor.factory.spec.ts
index 6c870d1ab..0f6e27ab1 100644
--- a/packages/composite-editor-component/src/compositeEditor.factory.spec.ts
+++ b/packages/composite-editor-component/src/compositeEditor.factory.spec.ts
@@ -28,6 +28,7 @@ const getEditControllerMock = {
 const gridStub = {
   autosizeColumns: jest.fn(),
   editActiveCell: jest.fn(),
+  focus: jest.fn(),
   getColumnIndex: jest.fn(),
   getActiveCell: jest.fn(),
   getCellNode: jest.fn(),