diff --git a/packages/devextreme/js/__internal/ui/m_tag_box.ts b/packages/devextreme/js/__internal/ui/m_tag_box.ts index 86177873114..0d4eb96d52b 100644 --- a/packages/devextreme/js/__internal/ui/m_tag_box.ts +++ b/packages/devextreme/js/__internal/ui/m_tag_box.ts @@ -1497,6 +1497,23 @@ const TagBox = (SelectBox as any).inherit({ delete this._tagTemplate; }, + _synchronizeTagsWithData() { + const dataSource = this.option('dataSource') || []; + const selectedItems = this.option('selectedItems') || []; + + if (!dataSource.length) { + this.option('selectedItems', []); + this.option('value', []); + return; + } + + const validSelectedItems = this._filterSelectedItems(dataSource, selectedItems); + const validValues = validSelectedItems.map((item) => this._valueGetter(item)); + + this.option('selectedItems', validSelectedItems); + this.option('value', validValues); + }, + _getSelectedItemsDifference(newItems, previousItems) { if (!newItems.length) { return { @@ -1592,6 +1609,10 @@ const TagBox = (SelectBox as any).inherit({ this.$element().toggleClass(TAGBOX_SINGLE_LINE_CLASS, !value); this._renderSingleLineScroll(); break; + case 'dataSource': + this._synchronizeTagsWithData(); + this._renderTags(); + break; case 'maxFilterQueryLength': break; default: diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js index 2d6c083b263..d9a4953f0de 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/tagBox.tests.js @@ -6693,6 +6693,58 @@ QUnit.module('dataSource integration', moduleSetup, () => { assert.ok(true, 'TagBox rendered'); }); + QUnit.module('Updating DataSource', { + beforeEach: function() { + this.data = [ + { ID: 1, Name: 'HD Video Player' }, + { ID: 2, Name: 'SuperHD Video Player' } + ]; + this.$tagBox = $('#tagBox').dxTagBox({ + dataSource: this.data, + value: [1, 2], + displayExpr: 'Name', + valueExpr: 'ID', + }); + this.tagBox = this.$tagBox.dxTagBox('instance'); + } + }, () => { + QUnit.test('Tagbox should only render existing tags (T1253312)', function(assert) { + this.tagBox.option('dataSource', this.data.slice(0, 1)); + + assert.strictEqual(this.$tagBox.find(`.${TAGBOX_TAG_CLASS}`).length, 1, 'Only one tag is rendered after the dataSource is reduced.'); + assert.strictEqual(this.$tagBox.find(`.${TAGBOX_TAG_CLASS}`).eq(0).text(), 'HD Video Player', 'Rendered tags match the updated dataSource.'); + }); + + QUnit.test('Tagbox updates value and selectedItems (T1253312)', function(assert) { + this.tagBox.option('dataSource', this.data.slice(0, 1)); + + assert.deepEqual(this.tagBox.option('selectedItems'), this.data.slice(0, 1), 'selectedItems match the updated dataSource.'); + assert.deepEqual(this.tagBox.option('value'), [1], 'value is updated based on the new dataSource.'); + }); + + QUnit.test('Tagbox shouldnt render any tags if dataSource is updated to either null or empty (T1253312)', function(assert) { + this.tagBox.option('dataSource', []); + + assert.strictEqual(this.$tagBox.find(`.${TAGBOX_TAG_CLASS}`).length, 0, 'No tags are rendered after empty dataSource'); + + this.tagBox.option('dataSource', null); + + assert.strictEqual(this.$tagBox.find(`.${TAGBOX_TAG_CLASS}`).length, 0, 'No tags are rendered after null dataSource'); + }); + + QUnit.test('Tagbox should update its selectedItems and value to empty if dataSource is updated to empty or null (T1253312)', function(assert) { + this.tagBox.option('dataSource', []); + + assert.deepEqual(this.tagBox.option('selectedItems'), [], 'selectedItems is updated to empty array when dataSource is empty'); + assert.deepEqual(this.tagBox.option('value'), [], 'value is updated to empty array when dataSource is empty'); + + this.tagBox.option('dataSource', null); + + assert.deepEqual(this.tagBox.option('selectedItems'), [], 'selectedItems is updated to empty array when dataSource is null'); + assert.deepEqual(this.tagBox.option('value'), [], 'value is updated to empty array when dataSource is null'); + }); + }); + QUnit.test('tags loading call result should be ignored after new call', function(assert) { const items = [{ id: 1, text: 'first' }, { id: 2, text: 'second' }]; const customStore = new CustomStore({