Skip to content

Commit 080f271

Browse files
authored
TagBox: should not trigger excess onSelectionChanged when value is not changed (T1259823)
1 parent 0caf424 commit 080f271

File tree

2 files changed

+100
-9
lines changed

2 files changed

+100
-9
lines changed

packages/devextreme/js/__internal/ui/m_tag_box.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,8 +1070,10 @@ class TagBox<
10701070

10711071
_renderTagsImpl(): void {
10721072
this._renderField();
1073-
// @ts-expect-error ts-error
1074-
this.option('selectedItems', this._selectedItems.slice());
1073+
if (this._shouldUpdateSelectedItems()) {
1074+
// @ts-expect-error ts-error
1075+
this.option('selectedItems', this._selectedItems.slice());
1076+
}
10751077
this._cleanTags();
10761078

10771079
const fieldTemplate = this._getFieldTemplate();
@@ -1156,6 +1158,22 @@ class TagBox<
11561158
this._popup?.refreshPosition();
11571159
}
11581160

1161+
_shouldUpdateSelectedItems(): boolean {
1162+
const { selectedItems } = this.option();
1163+
1164+
if (isDefined(selectedItems) && selectedItems.length !== this._selectedItems?.length) {
1165+
return true;
1166+
}
1167+
1168+
const intersection = getIntersection(selectedItems, this._selectedItems);
1169+
1170+
if (intersection.length !== this._selectedItems?.length) {
1171+
return true;
1172+
}
1173+
1174+
return false;
1175+
}
1176+
11591177
_renderTagsElements(items): void {
11601178
const $multiTag = this._multiTagRequired() && this._renderMultiTag(this._input());
11611179
const showMultiTagOnly = this.option('showMultiTagOnly');

packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -399,13 +399,15 @@ QUnit.module('list selection', moduleSetup, () => {
399399
let $item = getListItems(tagBox).eq(0);
400400

401401
$item.trigger('dxclick');
402-
assert.deepEqual(spy.args[1][0].addedItems, [1], 'added items is correct');
403-
assert.deepEqual(spy.args[1][0].removedItems, [], 'removed items is empty');
402+
403+
assert.deepEqual(spy.args[0][0].addedItems, [1], 'added items is correct');
404+
assert.deepEqual(spy.args[0][0].removedItems, [], 'removed items is empty');
404405

405406
$item = getListItems(tagBox).eq(1);
406407
$item.trigger('dxclick');
407-
assert.deepEqual(spy.args[2][0].addedItems, [3], 'added items is correct');
408-
assert.deepEqual(spy.args[2][0].removedItems, [], 'removed items is empty');
408+
409+
assert.deepEqual(spy.args[1][0].addedItems, [3], 'added items is correct');
410+
assert.deepEqual(spy.args[1][0].removedItems, [], 'removed items is empty');
409411
});
410412

411413
QUnit.test('selected items should be correct after item click with hideSelecterdItems option (T606462)', function(assert) {
@@ -5089,6 +5091,77 @@ QUnit.module('the \'selectedItems\' option', moduleSetup, () => {
50895091
assert.strictEqual(selectionChangedHandler.callCount, callCountOnInit + 1, 'onSelectionChanged handler was called');
50905092
});
50915093

5094+
QUnit.test('onSelectionChanged should be called if value is specified on init', function(assert) {
5095+
const selectionChangedHandler = sinon.spy();
5096+
5097+
$('#tagBox').dxTagBox({
5098+
items: [1, 2, 3],
5099+
value: [1],
5100+
onSelectionChanged: selectionChangedHandler
5101+
});
5102+
5103+
assert.strictEqual(selectionChangedHandler.callCount, 1, 'onSelectionChanged handler was called on init');
5104+
});
5105+
5106+
QUnit.test('onSelectionChanged should not be called if value is not specified on init (T1259823)', function(assert) {
5107+
const selectionChangedHandler = sinon.spy();
5108+
5109+
$('#tagBox').dxTagBox({
5110+
items: [1, 2, 3],
5111+
onSelectionChanged: selectionChangedHandler
5112+
});
5113+
5114+
assert.strictEqual(selectionChangedHandler.callCount, 0, 'onSelectionChanged handler was not called on init');
5115+
});
5116+
5117+
QUnit.test('onSelectionChanged should not be called on runtime items change if new items include selected items (T1259823)', function(assert) {
5118+
const selectionChangedHandler = sinon.spy();
5119+
5120+
const tagBox = $('#tagBox').dxTagBox({
5121+
items: [1, 2, 3],
5122+
value: [1],
5123+
onSelectionChanged: selectionChangedHandler
5124+
}).dxTagBox('instance');
5125+
5126+
assert.strictEqual(selectionChangedHandler.callCount, 1, 'onSelectionChanged handler was called on init');
5127+
5128+
tagBox.option('items', [1, 2]);
5129+
5130+
assert.strictEqual(selectionChangedHandler.callCount, 1, 'onSelectionChanged handler was not called on items change');
5131+
});
5132+
5133+
QUnit.test('onSelectionChanged should be called on runtime items change if new items do not include selected items', function(assert) {
5134+
const selectionChangedHandler = sinon.spy();
5135+
5136+
const tagBox = $('#tagBox').dxTagBox({
5137+
items: [1, 2, 3],
5138+
value: [1],
5139+
onSelectionChanged: selectionChangedHandler
5140+
}).dxTagBox('instance');
5141+
5142+
assert.strictEqual(selectionChangedHandler.callCount, 1, 'onSelectionChanged handler was called on init');
5143+
5144+
tagBox.option('items', [2, 3]);
5145+
5146+
assert.strictEqual(selectionChangedHandler.callCount, 2, 'onSelectionChanged handler was called on items change');
5147+
});
5148+
5149+
QUnit.test('onSelectionChanged should not be called on runtime value change on the same value (T1259823)', function(assert) {
5150+
const selectionChangedHandler = sinon.spy();
5151+
5152+
const tagBox = $('#tagBox').dxTagBox({
5153+
items: [1, 2, 3],
5154+
value: [1],
5155+
onSelectionChanged: selectionChangedHandler
5156+
}).dxTagBox('instance');
5157+
5158+
assert.strictEqual(selectionChangedHandler.callCount, 1, 'onSelectionChanged handler was called on init');
5159+
5160+
tagBox.option('value', [1]);
5161+
5162+
assert.strictEqual(selectionChangedHandler.callCount, 1, 'onSelectionChanged handler was not called on value change');
5163+
});
5164+
50925165
QUnit.test('The \'selectedItems\' option changes after the \'value\' option', function(assert) {
50935166
const items = [1, 2, 3];
50945167

@@ -5181,13 +5254,13 @@ QUnit.module('the \'onSelectionChanged\' option', moduleSetup, () => {
51815254
const $listItems = tagBox._list.$element().find('.dx-list-item');
51825255

51835256
$($listItems.eq(0)).trigger('dxclick');
5184-
assert.deepEqual(spy.args[1][0].addedItems, [items[0]], 'first item is in the \'addedItems\' argument');
5257+
assert.deepEqual(spy.args[0][0].addedItems, [items[0]], 'first item is in the \'addedItems\' argument');
51855258

51865259
$($listItems.eq(1)).trigger('dxclick');
5187-
assert.deepEqual(spy.args[2][0].addedItems, [items[1]], 'second item is in the \'addedItems\' argument');
5260+
assert.deepEqual(spy.args[1][0].addedItems, [items[1]], 'second item is in the \'addedItems\' argument');
51885261

51895262
$($listItems.eq(1)).trigger('dxclick');
5190-
assert.deepEqual(spy.args[3][0].addedItems, [], 'no items in the \'addedItems\' argument after item is unselected');
5263+
assert.deepEqual(spy.args[2][0].addedItems, [], 'no items in the \'addedItems\' argument after item is unselected');
51915264
});
51925265

51935266
QUnit.test('the \'onSelectionChanged\' action should contain correct \'removedItems\' argument', function(assert) {

0 commit comments

Comments
 (0)