From 85dfd43dae45a2ee3c9f9561603d85a43f917d6f Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Tue, 21 Jul 2026 12:29:20 +0200 Subject: [PATCH 1/6] update quill version --- pnpm-lock.yaml | 16 ++++++++-------- pnpm-workspace.yaml | 21 ++------------------- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d31c3c597ef0..cefc347808fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,8 +92,8 @@ catalogs: specifier: 4.4.11 version: 4.4.11 devextreme-quill: - specifier: 1.7.10 - version: 1.7.10 + specifier: 1.8.0 + version: 1.8.0 eslint: specifier: 9.39.4 version: 9.39.4 @@ -447,7 +447,7 @@ importers: version: 4.4.11 devextreme-quill: specifier: 'catalog:' - version: 1.7.10 + version: 1.8.0 devextreme-react: specifier: workspace:* version: link:../../packages/devextreme-react/npm @@ -1222,7 +1222,7 @@ importers: version: link:../../packages/devextreme/artifacts/npm/devextreme-dist devextreme-quill: specifier: 'catalog:' - version: 1.7.10 + version: 1.8.0 devextreme-react: specifier: workspace:~ version: link:../../packages/devextreme-react/npm @@ -1367,7 +1367,7 @@ importers: version: 4.1.69 devextreme-quill: specifier: 'catalog:' - version: 1.7.10 + version: 1.8.0 inferno: specifier: 'catalog:' version: 8.2.3 @@ -9772,8 +9772,8 @@ packages: engines: {node: ^24.16.0} hasBin: true - devextreme-quill@1.7.10: - resolution: {integrity: sha512-AsbMuYAON87Xk77bY4wjjtniONrgQe/WV7LSFxdU58z+bBn0gCkH3hZOJorSwfotThcwRLb150oTJ2ABJcO6DQ==} + devextreme-quill@1.8.0: + resolution: {integrity: sha512-qdSVCjh2d5qEb8nCRzTerjhnfFuzrftnErsgsQzzLbJXaZpQgQfhyH3HC9YpAC3Pgc9HZsI0eEAOlDcGm4YSzw==} devextreme-schematics@1.14.2: resolution: {integrity: sha512-kT2Gf1XpQeG72x4q/SLiFvTrD2wswyrojSkxLWlWwRQZ3kGauFRVg3CPu6s2zZ1eJc705N8W38fwgEgN+nONnA==} @@ -27861,7 +27861,7 @@ snapshots: typescript: 5.9.3 winston: 3.19.0 - devextreme-quill@1.7.10: + devextreme-quill@1.8.0: dependencies: core-js: 3.49.0 eventemitter3: 4.0.7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c36e624bfb1a..917638e56e09 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -60,7 +60,7 @@ catalog: devexpress-diagram: 2.2.29 devexpress-gantt: 4.1.69 devextreme-exceljs-fork: 4.4.11 - devextreme-quill: 1.7.10 + devextreme-quill: 1.8.0 "@eslint/js": 9.39.4 eslint: 9.39.4 eslint-config-airbnb-typescript: 18.0.0 @@ -134,21 +134,4 @@ patchedDependencies: babel-core@6.26.3: patches/babel-core@6.26.3.patch minimumReleaseAgeExclude: - - "eslint-config-devextreme" - - devextreme-quill@1.7.10 - - '@nx/devkit@23.0.1' - - '@nx/jest@23.0.1' - - '@nx/js@23.0.1' - - '@nx/nx-darwin-arm64@23.0.1' - - '@nx/nx-darwin-x64@23.0.1' - - '@nx/nx-freebsd-x64@23.0.1' - - '@nx/nx-linux-arm-gnueabihf@23.0.1' - - '@nx/nx-linux-arm64-gnu@23.0.1' - - '@nx/nx-linux-arm64-musl@23.0.1' - - '@nx/nx-linux-x64-gnu@23.0.1' - - '@nx/nx-linux-x64-musl@23.0.1' - - '@nx/nx-win32-arm64-msvc@23.0.1' - - '@nx/nx-win32-x64-msvc@23.0.1' - - '@nx/workspace@23.0.1' - - nx@23.0.1 - - devextreme-internal-tools@22.0.0 + - devextreme-quill@1.8.0 From db56f8ec4696e4ae478b6942c100f286f8880481 Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Tue, 21 Jul 2026 12:42:40 +0200 Subject: [PATCH 2/6] update --- pnpm-workspace.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 917638e56e09..172bee9fe966 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,6 +9,7 @@ preferWorkspacePackages: true saveWorkspaceProtocol: false packageManagerStrict: false engineStrict: true +minimumReleaseAge: 0 overrides: "basic-ftp@<5.3.1": ">=5.3.1" @@ -132,6 +133,3 @@ patchedDependencies: testcafe-hammerhead@31.7.8: patches/testcafe-hammerhead@31.7.8.patch systemjs-builder@0.16.15: patches/systemjs-builder@0.16.15.patch babel-core@6.26.3: patches/babel-core@6.26.3.patch - -minimumReleaseAgeExclude: - - devextreme-quill@1.8.0 From f983de3b0ba36c15509f3c478db276c8ff69b52d Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Tue, 21 Jul 2026 13:58:06 +0200 Subject: [PATCH 3/6] add a quill option --- .../__tests__/focus_escape.test.ts | 36 +++++++++++++++++++ .../__internal/ui/html_editor/html_editor.ts | 3 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts index ce3db2c637a8..8bc061935542 100644 --- a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts +++ b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts @@ -100,3 +100,39 @@ describe('HtmlEditor focus escape (Ctrl+Shift+Up/Down)', () => { expect(event.defaultPrevented).toBe(false); }); }); + +describe('HtmlEditor Tab key handling (inlineTabInsertion)', () => { + afterEach(() => { + editors.forEach((instance) => instance.dispose()); + editors.length = 0; + document.body.innerHTML = ''; + }); + + it('Tab leaves the editor by default', () => { + const model = createEditor(); + const instance = editors[editors.length - 1]; + const quill = instance.getQuillInstance(); + quill.setSelection(0, 0); + + const event = model.pressKeyInContent('Tab'); + + expect(event.defaultPrevented).toBe(false); + expect(quill.getText()).not.toContain('\t'); + }); + + it('Tab inserts a tab character when inlineTabInsertion is enabled', () => { + const model = createEditor({ + customizeModules: (modules) => { + modules.keyboard.inlineTabInsertion = true; + }, + }); + const instance = editors[editors.length - 1]; + const quill = instance.getQuillInstance(); + quill.setSelection(0, 0); + + const event = model.pressKeyInContent('Tab'); + + expect(event.defaultPrevented).toBe(true); + expect(quill.getText()).toContain('\t'); + }); +}); diff --git a/packages/devextreme/js/__internal/ui/html_editor/html_editor.ts b/packages/devextreme/js/__internal/ui/html_editor/html_editor.ts index f79315cd5627..88008acdb9f7 100644 --- a/packages/devextreme/js/__internal/ui/html_editor/html_editor.ts +++ b/packages/devextreme/js/__internal/ui/html_editor/html_editor.ts @@ -499,9 +499,10 @@ class HtmlEditor extends Editor { }; } - _getKeyboardModuleConfig(): { onKeydown: (e: DxEvent) => void } { + _getKeyboardModuleConfig(): { onKeydown: (e: DxEvent) => void, inlineTabInsertion: boolean } { return { onKeydown: (e) => this._saveValueChangeEvent(dxEvent(e)), + inlineTabInsertion: false, }; } From b66f39cb45f1570513dea7c99a6bd2b0406eff93 Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Tue, 21 Jul 2026 14:06:47 +0200 Subject: [PATCH 4/6] fix tests --- .../__internal/ui/html_editor/__tests__/focus_escape.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts index 8bc061935542..fde1116839f0 100644 --- a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts +++ b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts @@ -108,7 +108,7 @@ describe('HtmlEditor Tab key handling (inlineTabInsertion)', () => { document.body.innerHTML = ''; }); - it('Tab leaves the editor by default', () => { + it('should leave the editor on Tab keypress by default', () => { const model = createEditor(); const instance = editors[editors.length - 1]; const quill = instance.getQuillInstance(); @@ -120,7 +120,7 @@ describe('HtmlEditor Tab key handling (inlineTabInsertion)', () => { expect(quill.getText()).not.toContain('\t'); }); - it('Tab inserts a tab character when inlineTabInsertion is enabled', () => { + it('should insert a tab character on Tab keypress when inlineTabInsertion is enabled', () => { const model = createEditor({ customizeModules: (modules) => { modules.keyboard.inlineTabInsertion = true; From a61421d9f42230630566ff6acdb0904caead9c25 Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Tue, 21 Jul 2026 15:04:21 +0200 Subject: [PATCH 5/6] fix tests --- .../ui/__tests__/__mock__/model/html_editor.ts | 11 +++++++++++ .../ui/html_editor/__tests__/focus_escape.test.ts | 8 +++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts b/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts index 96f9355e0505..1aca1238e283 100644 --- a/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts +++ b/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts @@ -1,3 +1,5 @@ +import HtmlEditor from '@ts/ui/html_editor/html_editor'; + const CLASSES = { content: 'dx-htmleditor-content', toolbarWrapper: 'dx-htmleditor-toolbar-wrapper', @@ -10,6 +12,15 @@ export class HtmlEditorModel { return this.root.querySelector(`.${CLASSES.content}`) as HTMLElement; } + public getInstance(): HtmlEditor { + return HtmlEditor.getInstance(this.root); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public getQuillInstance(): any { + return this.getInstance().getQuillInstance(); + } + public getToolbarWrapper(): HTMLElement { return this.root.querySelector(`.${CLASSES.toolbarWrapper}`) as HTMLElement; } diff --git a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts index fde1116839f0..066fe60c9ab9 100644 --- a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts +++ b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts @@ -108,10 +108,9 @@ describe('HtmlEditor Tab key handling (inlineTabInsertion)', () => { document.body.innerHTML = ''; }); - it('should leave the editor on Tab keypress by default', () => { + it('should not paste the /t symbol in editor on Tab keypress by default', () => { const model = createEditor(); - const instance = editors[editors.length - 1]; - const quill = instance.getQuillInstance(); + const quill = model.getQuillInstance(); quill.setSelection(0, 0); const event = model.pressKeyInContent('Tab'); @@ -126,8 +125,7 @@ describe('HtmlEditor Tab key handling (inlineTabInsertion)', () => { modules.keyboard.inlineTabInsertion = true; }, }); - const instance = editors[editors.length - 1]; - const quill = instance.getQuillInstance(); + const quill = model.getQuillInstance(); quill.setSelection(0, 0); const event = model.pressKeyInContent('Tab'); From 0ce3d5c99aa903847060a9606237c3e7301fe10e Mon Sep 17 00:00:00 2001 From: Andrei Kharitonov Date: Tue, 21 Jul 2026 15:18:09 +0200 Subject: [PATCH 6/6] fix tests --- .../__internal/ui/__tests__/__mock__/model/html_editor.ts | 7 +++++++ .../ui/html_editor/__tests__/focus_escape.test.ts | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts b/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts index 1aca1238e283..782c55b239a1 100644 --- a/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts +++ b/packages/devextreme/js/__internal/ui/__tests__/__mock__/model/html_editor.ts @@ -43,4 +43,11 @@ export class HtmlEditorModel { return event; } + + // Since Jest doesn't have native events, Quill can't process + // click on itself to focus and do all its internal work. + // So we need to set selection manually to simulate focus. + public setQuillFocus(): void { + this.getQuillInstance().setSelection(0, 0); + } } diff --git a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts index 066fe60c9ab9..75617adc83c9 100644 --- a/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts +++ b/packages/devextreme/js/__internal/ui/html_editor/__tests__/focus_escape.test.ts @@ -111,7 +111,9 @@ describe('HtmlEditor Tab key handling (inlineTabInsertion)', () => { it('should not paste the /t symbol in editor on Tab keypress by default', () => { const model = createEditor(); const quill = model.getQuillInstance(); - quill.setSelection(0, 0); + model.setQuillFocus(); + + expect(quill.getText()).not.toContain('\t'); const event = model.pressKeyInContent('Tab'); @@ -126,8 +128,9 @@ describe('HtmlEditor Tab key handling (inlineTabInsertion)', () => { }, }); const quill = model.getQuillInstance(); - quill.setSelection(0, 0); + model.setQuillFocus(); + expect(quill.getText()).not.toContain('\t'); const event = model.pressKeyInContent('Tab'); expect(event.defaultPrevented).toBe(true);