From b59197b841d2a42cc044993a61fe55ffdba22027 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 28 Jan 2026 16:32:55 +0200 Subject: [PATCH 1/6] add early exit to moveFocusedItem function --- packages/devextreme/js/__internal/ui/list/list.edit.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/devextreme/js/__internal/ui/list/list.edit.ts b/packages/devextreme/js/__internal/ui/list/list.edit.ts index c2d52af758be..aa2a0bd37f52 100644 --- a/packages/devextreme/js/__internal/ui/list/list.edit.ts +++ b/packages/devextreme/js/__internal/ui/list/list.edit.ts @@ -63,6 +63,10 @@ class ListEdit extends ListBase { const nextItemIndex = focusedItemIndex + (moveUp ? -1 : 1); const $nextItem = editStrategy.getItemElement(nextItemIndex); + if (!$nextItem) { + return; + } + const isMoveFromGroup = grouped && $(focusedElement).parent().get(0) !== $nextItem.parent().get(0); if (!isMoveFromGroup) { From 4274646926947fb0564a68b57cd0999e6ef53cca Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 28 Jan 2026 16:33:46 +0200 Subject: [PATCH 2/6] add tests for this case --- .../listParts/editingTests.js | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js index c69073f59068..c4d9bd229609 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js @@ -319,6 +319,53 @@ QUnit.module('keyboard navigation', { assert.deepEqual(list.option('items'), items, 'items were reordered'); }); + QUnit.test('shift+arrowUp on first item should not throw error', function(assert) { + const items = ['1', '2', '3']; + + const $list = $('#list').dxList({ + items: items, + editEnabled: true, + itemDragging: { + allowReordering: true + }, + focusStateEnabled: true + }); + const list = $list.dxList('instance'); + const keyboard = keyboardMock($list.find('[tabindex=0]')); + + const $firstItem = $list.find('.' + LIST_ITEM_CLASS).eq(0); + $firstItem.trigger('dxpointerdown'); + this.clock.tick(10); + + keyboard.keyDown('arrowUp', { shiftKey: true }); + + assert.deepEqual(list.option('items'), items, 'items order is unchanged'); + }); + + QUnit.test('shift+arrowDown on last item should not throw error', function(assert) { + const items = ['1', '2', '3']; + + const $list = $('#list').dxList({ + items: items, + editEnabled: true, + itemDragging: { + allowReordering: true + }, + focusStateEnabled: true + }); + const list = $list.dxList('instance'); + const keyboard = keyboardMock($list.find('[tabindex=0]')); + + const $lastItem = $list.find('.' + LIST_ITEM_CLASS).eq(2); + $lastItem.trigger('dxpointerdown'); + this.clock.tick(10); + + keyboard.keyDown('arrowDown', { shiftKey: true }); + + assert.deepEqual(list.option('items'), items, 'items order is unchanged'); + }); + + QUnit.module('grouped', { beforeEach: function() { this.getInitialItems = () => { From 38d46aee962f05499e7251d13bb86430080ea2a1 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Thu, 29 Jan 2026 17:49:08 +0200 Subject: [PATCH 3/6] fix by review comments --- .../listParts/editingTests.js | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js index c4d9bd229609..93200f32f07e 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js @@ -4,6 +4,7 @@ import executeAsyncMock from '../../../helpers/executeAsyncMock.js'; import keyboardMock from '../../../helpers/keyboardMock.js'; import { DataSource } from 'common/data/data_source/data_source'; import ArrayStore from 'common/data/array_store'; +import consoleUtils from 'core/utils/console'; import 'ui/list'; @@ -319,7 +320,7 @@ QUnit.module('keyboard navigation', { assert.deepEqual(list.option('items'), items, 'items were reordered'); }); - QUnit.test('shift+arrowUp on first item should not throw error', function(assert) { + QUnit.test('shift+arrowUp on first item should not throw error (T1320189)', function(assert) { const items = ['1', '2', '3']; const $list = $('#list').dxList({ @@ -330,19 +331,24 @@ QUnit.module('keyboard navigation', { }, focusStateEnabled: true }); + const list = $list.dxList('instance'); const keyboard = keyboardMock($list.find('[tabindex=0]')); const $firstItem = $list.find('.' + LIST_ITEM_CLASS).eq(0); - $firstItem.trigger('dxpointerdown'); - this.clock.tick(10); + list.option('focusedElement', $firstItem.get(0)); - keyboard.keyDown('arrowUp', { shiftKey: true }); + let errorMessage = ''; + try { + keyboard.keyDown('arrowUp', { shiftKey: true }); + } catch(e) { + errorMessage = `error message: ${e.message}`; + } - assert.deepEqual(list.option('items'), items, 'items order is unchanged'); + assert.strictEqual(errorMessage, '', 'no error thrown when trying to move first item up'); }); - QUnit.test('shift+arrowDown on last item should not throw error', function(assert) { + QUnit.test('shift+arrowDown on last item should not throw error (T1320189)', function(assert) { const items = ['1', '2', '3']; const $list = $('#list').dxList({ @@ -357,12 +363,16 @@ QUnit.module('keyboard navigation', { const keyboard = keyboardMock($list.find('[tabindex=0]')); const $lastItem = $list.find('.' + LIST_ITEM_CLASS).eq(2); - $lastItem.trigger('dxpointerdown'); - this.clock.tick(10); + list.option('focusedElement', $lastItem.get(0)); - keyboard.keyDown('arrowDown', { shiftKey: true }); + let errorMessage = ''; + try { + keyboard.keyDown('arrowDown', { shiftKey: true }); + } catch(e) { + errorMessage = `error message: ${e.message}`; + } - assert.deepEqual(list.option('items'), items, 'items order is unchanged'); + assert.strictEqual(errorMessage, '', 'no error thrown when trying to move first item up'); }); From 6f0c38d87019fc16f2d808fe542f54222159d7d3 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Thu, 29 Jan 2026 17:55:56 +0200 Subject: [PATCH 4/6] remove extra import --- .../tests/DevExpress.ui.widgets/listParts/editingTests.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js index 93200f32f07e..d165691cf86e 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js @@ -4,7 +4,6 @@ import executeAsyncMock from '../../../helpers/executeAsyncMock.js'; import keyboardMock from '../../../helpers/keyboardMock.js'; import { DataSource } from 'common/data/data_source/data_source'; import ArrayStore from 'common/data/array_store'; -import consoleUtils from 'core/utils/console'; import 'ui/list'; From 2a0f355b875d6d5d7132db4abd393ad560f65a3f Mon Sep 17 00:00:00 2001 From: dmlvr Date: Thu, 29 Jan 2026 18:14:59 +0200 Subject: [PATCH 5/6] rename result description for shift+arrowDown test --- .../tests/DevExpress.ui.widgets/listParts/editingTests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js index d165691cf86e..5aa52336ee8a 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/editingTests.js @@ -371,7 +371,7 @@ QUnit.module('keyboard navigation', { errorMessage = `error message: ${e.message}`; } - assert.strictEqual(errorMessage, '', 'no error thrown when trying to move first item up'); + assert.strictEqual(errorMessage, '', 'no error thrown when trying to move last item down'); }); From ff867c6e8421755ec0950a22fd32ad37402db903 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Thu, 29 Jan 2026 18:49:50 +0200 Subject: [PATCH 6/6] add some additional early return basen on index checking --- packages/devextreme/js/__internal/ui/list/list.edit.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/devextreme/js/__internal/ui/list/list.edit.ts b/packages/devextreme/js/__internal/ui/list/list.edit.ts index aa2a0bd37f52..c9bb0b094886 100644 --- a/packages/devextreme/js/__internal/ui/list/list.edit.ts +++ b/packages/devextreme/js/__internal/ui/list/list.edit.ts @@ -61,6 +61,13 @@ class ListEdit extends ListBase { if (e.shiftKey && itemDragging?.allowReordering) { const nextItemIndex = focusedItemIndex + (moveUp ? -1 : 1); + + if (nextItemIndex < 0 + || nextItemIndex === NOT_EXISTING_INDEX + || nextItemIndex > this._getLastItemIndex()) { + return; + } + const $nextItem = editStrategy.getItemElement(nextItemIndex); if (!$nextItem) {