From 3d4c09b0d4fa04c5ba9b5d5a3d1a058b8014d9c3 Mon Sep 17 00:00:00 2001 From: OlgaLarina Date: Wed, 4 Oct 2023 16:38:49 +0300 Subject: [PATCH] resolve #7043 Dropdown with Lazy loading throws Duplicate keys detected errors Vue2 --- src/dropdownListModel.ts | 5 +- tests/questionDropdownTests.ts | 119 +++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/dropdownListModel.ts b/src/dropdownListModel.ts index bd8ed7030f..d74607a7a3 100644 --- a/src/dropdownListModel.ts +++ b/src/dropdownListModel.ts @@ -191,6 +191,7 @@ export class DropdownListModel extends Base { this.resetFilterString(); } protected onSetFilterString(): void { + if (!this.filterString && !this.popupModel.isVisible) return; if (!!this.filterString && !this.popupModel.isVisible) { this.popupModel.isVisible = true; } @@ -508,11 +509,11 @@ export class DropdownListModel extends Base { this._popupModel.isVisible = true; return; } + doKey2ClickBlur(event); + this._popupModel.isVisible = false; this.resetFilterString(); this.inputString = null; this.hintString = ""; - doKey2ClickBlur(event); - this._popupModel.isVisible = false; event.stopPropagation(); } onFocus(event: any): void { diff --git a/tests/questionDropdownTests.ts b/tests/questionDropdownTests.ts index 700ae34dc5..56240fffef 100644 --- a/tests/questionDropdownTests.ts +++ b/tests/questionDropdownTests.ts @@ -1744,4 +1744,123 @@ QUnit.test("lazy loading: change choicesLazyLoadEnabled on runtime", assert => { assert.equal(question.choices.length, 5); assert.equal(question.dropdownListModel["listModel"].visibleItems.length, 6, "#3"); assert.equal(question.dropdownListModel["listModel"].visibleItems[5].id, "loadingIndicator"); +}); +QUnit.test("lazy loading: duplication of elements after blur", assert => { + const done = assert.async(4); + const json = { + questions: [{ + "type": "dropdown", + "name": "q1", + "choicesLazyLoadEnabled": true, + "choicesLazyLoadPageSize": 25, + }] + }; + const survey = new SurveyModel(json); + survey.onChoicesLazyLoad.add(callback); + const question = survey.getAllQuestions()[0]; + const itemsSettings = question.dropdownListModel["itemsSettings"]; + + assert.equal(question.choicesLazyLoadEnabled, true); + assert.equal(question.choices.length, 0, "question.choices.length #0"); + question.dropdownListModel.popupModel.isVisible = true; + question.dropdownListModel.changeSelectionWithKeyboard(false); + setTimeout(() => { + assert.equal(question.choices.length, 25, "question.choices.length #1"); + assert.equal(question.dropdownListModel.inputStringRendered, "", "question.dropdownListModel.inputStringRendered #1"); + assert.equal(itemsSettings.skip, 25, "skip #1"); + assert.equal(itemsSettings.take, 25, "take #1"); + assert.equal(itemsSettings.totalCount, 55, "totalCount #1"); + assert.equal(itemsSettings.items.length, 25, "items.length #1"); + question.dropdownListModel.inputStringRendered = "11"; + + setTimeout(() => { + assert.equal(question.choices.length, 25, "question.choices.length #2"); + assert.equal(question.dropdownListModel.inputStringRendered, "11", "question.dropdownListModel.inputStringRendered #2"); + assert.equal(itemsSettings.skip, 25, "skip #2"); + assert.equal(itemsSettings.take, 25, "take #2"); + assert.equal(itemsSettings.totalCount, 55, "totalCount #2"); + assert.equal(itemsSettings.items.length, 25, "items.length #2"); + question.dropdownListModel.onBlur({ + keyCode: 0, + preventDefault: () => { }, + stopPropagation: () => { } + }); + + setTimeout(() => { + assert.equal(question.value, undefined); + assert.equal(question.choices.length, 25, "question.choices.length #3"); + assert.equal(question.dropdownListModel.inputStringRendered, "", "question.dropdownListModel.inputStringRendered #3"); + assert.equal(itemsSettings.skip, 0, "skip #3"); + assert.equal(itemsSettings.take, 25, "take #3"); + assert.equal(itemsSettings.totalCount, 0, "totalCount #3"); + assert.equal(itemsSettings.items.length, 0, "items.length #3"); + question.dropdownListModel.popupModel.isVisible = true; + + setTimeout(() => { + assert.equal(question.choices.length, 25, "question.choices.length #4"); + assert.equal(question.dropdownListModel.inputStringRendered, "", "question.dropdownListModel.inputStringRendered #4"); + + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); +}); +QUnit.test("lazy loading: check onChoicesLazyLoad callback count", assert => { + const done = assert.async(4); + let callbackCount = 0; + const json = { + questions: [{ + "type": "dropdown", + "name": "q1", + "choicesLazyLoadEnabled": true, + "choicesLazyLoadPageSize": 25, + }] + }; + const survey = new SurveyModel(json); + survey.onChoicesLazyLoad.add((_, opt) => { + const total = 55; + setTimeout(() => { + callbackCount++; + if(opt.skip + opt.take < total) { + opt.setItems(getNumberArray(opt.skip + 1, opt.take, opt.filter), total); + } else { + opt.setItems(getNumberArray(opt.skip + 1, total - opt.skip, opt.filter), total); + } + }, onChoicesLazyLoadCallbackTimeOut); }); + const question = survey.getAllQuestions()[0]; + + assert.equal(callbackCount, 0); + question.dropdownListModel.popupModel.isVisible = true; + question.dropdownListModel.changeSelectionWithKeyboard(false); + setTimeout(() => { + assert.equal(callbackCount, 1); + question.dropdownListModel.inputStringRendered = "11"; + + setTimeout(() => { + assert.equal(callbackCount, 2); + question.dropdownListModel.onBlur({ + keyCode: 0, + preventDefault: () => { }, + stopPropagation: () => { } + }); + + setTimeout(() => { + assert.equal(callbackCount, 2); + question.dropdownListModel.popupModel.isVisible = true; + + setTimeout(() => { + assert.equal(callbackCount, 3); + + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + done(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); }); \ No newline at end of file