From bc9c96b39c0e23373c03df97039670e00664a6dd Mon Sep 17 00:00:00 2001 From: OlgaLarina Date: Thu, 19 Oct 2023 18:10:58 +0300 Subject: [PATCH 1/2] maxSelectedChoices doesn't work correctly when tagbox with the lazy loading --- tests/question_tagbox_tests.ts | 127 +++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/tests/question_tagbox_tests.ts b/tests/question_tagbox_tests.ts index 346f19bf51..19f29113f2 100644 --- a/tests/question_tagbox_tests.ts +++ b/tests/question_tagbox_tests.ts @@ -1320,3 +1320,130 @@ QUnit.test("question.showClearButton", assert => { assert.equal(q.showClearButton, true, "Creator V2"); settings.supportCreatorV2 = false; }); +QUnit.test("lazy loading: maxSelectedChoices limit stops working if you clear the value", assert => { + const done1 = assert.async(); + const done2 = assert.async(); + const json = { + questions: [{ + "type": "tagbox", + "name": "q1", + "defaultValue": [1], + "choicesLazyLoadEnabled": true, + "choicesLazyLoadPageSize": 30, + "maxSelectedChoices": 2 + }] + }; + const survey = new SurveyModel(json); + survey.onChoicesLazyLoad.add(callback); + + const question = survey.getAllQuestions()[0]; + const dropdownListModel = question.dropdownListModel; + const list: MultiSelectListModel = dropdownListModel.popupModel.contentComponentData.model as MultiSelectListModel; + assert.equal(question.choicesLazyLoadEnabled, true); + assert.equal(question.choices.length, 0); + + question.dropdownListModel.popupModel.toggleVisibility(); + setTimeout(() => { + assert.deepEqual(question.value, [1]); + assert.equal(question.choices.length, 30); + for(let index = 0; index < list.actions.length - 1; index++) { + assert.ok(list.actions[index].enabled, list.actions[index].id + " is enabled before clear"); + } + + list.onItemClick(list.actions[1]); + assert.deepEqual(question.value, [1, 2]); + assert.ok(list.actions[0].enabled, "action 1 is enabled before clear"); + assert.ok(list.actions[1].enabled, "action 2 is enabled before clear"); + for(let index = 2; index < list.actions.length - 1; index++) { + assert.notOk(list.actions[index].enabled, list.actions[index].id + " is disabled before clear"); + } + question.dropdownListModel.popupModel.isVisible = false; + question.dropdownListModel.onClear({ + keyCode: 0, + preventDefault: () => { }, + stopPropagation: () => { } + }); + + question.dropdownListModel.popupModel.toggleVisibility(); + setTimeout(() => { + assert.deepEqual(question.value, [], "question value is empty"); + list.onItemClick(list.actions[0]); + assert.deepEqual(question.value, [1], "question value is [1]"); + + for(let index = 0; index < list.actions.length - 1; index++) { + assert.ok(list.actions[index].enabled, list.actions[index].id + " is enabled after clear"); + } + + list.onItemClick(list.actions[1]); + assert.deepEqual(question.value, [1, 2], "question value is [1, 2] after clear"); + assert.ok(list.actions[0].enabled, "action 1 is enabled after clear"); + assert.ok(list.actions[1].enabled, "action 2 is enabled after clear"); + for(let index = 2; index < list.actions.length - 1; index++) { + assert.notOk(list.actions[index].enabled, list.actions[index].id + " is disabled after clear"); + } + + done2(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + + done1(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); +}); +QUnit.test("lazy loading & maxSelectedChoices: Items remains disabled when unselecting choices within a drop-down list", assert => { + const done1 = assert.async(); + const done2 = assert.async(); + const json = { + questions: [{ + "type": "tagbox", + "name": "q1", + "defaultValue": [1], + "choicesLazyLoadEnabled": true, + "choicesLazyLoadPageSize": 30, + "maxSelectedChoices": 2 + }] + }; + const survey = new SurveyModel(json); + survey.onChoicesLazyLoad.add(callback); + + const question = survey.getAllQuestions()[0]; + const dropdownListModel = question.dropdownListModel; + const list: MultiSelectListModel = dropdownListModel.popupModel.contentComponentData.model as MultiSelectListModel; + assert.equal(question.choicesLazyLoadEnabled, true); + assert.equal(question.choices.length, 0); + + question.dropdownListModel.popupModel.toggleVisibility(); + setTimeout(() => { + assert.deepEqual(question.value, [1]); + assert.equal(question.choices.length, 30); + for(let index = 0; index < list.actions.length - 1; index++) { + assert.ok(list.actions[index].enabled, list.actions[index].id + " is enabled before unselecting choice"); + } + + list.onItemClick(list.actions[1]); + assert.deepEqual(question.value, [1, 2]); + assert.ok(list.actions[0].enabled, "action 1 is enabled before unselecting choice"); + assert.ok(list.actions[1].enabled, "action 2 is enabled before unselecting choice"); + for(let index = 2; index < list.actions.length - 1; index++) { + assert.notOk(list.actions[index].enabled, list.actions[index].id + " is disabled before unselecting choice"); + } + question.dropdownListModel.popupModel.isVisible = false; + question.dropdownListModel.popupModel.toggleVisibility(); + setTimeout(() => { + assert.deepEqual(question.value, [1, 2], "question value is [1, 2]"); + assert.ok(list.actions[0].enabled, "action 1 is enabled"); + assert.ok(list.actions[1].enabled, "action 2 is enabled"); + for(let index = 2; index < list.actions.length - 1; index++) { + assert.notOk(list.actions[index].enabled, list.actions[index].id + " is disabled"); + } + + list.onItemClick(list.actions[1]); + assert.deepEqual(question.value, [1], "question value is [1]"); + for(let index = 0; index < list.actions.length - 1; index++) { + assert.ok(list.actions[index].enabled, list.actions[index].id + " is enabled after unselecting choice"); + } + + done2(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); + + done1(); + }, onChoicesLazyLoadCallbackTimeOut + callbackTimeOutDelta); +}); \ No newline at end of file From cc409d9f7fffc42d528a5b2b75706d1aa34d5163 Mon Sep 17 00:00:00 2001 From: Andrew Telnov Date: Fri, 20 Oct 2023 14:21:36 +0300 Subject: [PATCH 2/2] FIx unit tests --- src/base.ts | 5 +++-- src/question_baseselect.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/base.ts b/src/base.ts index d549f5f207..29b3ee9ce7 100644 --- a/src/base.ts +++ b/src/base.ts @@ -564,8 +564,9 @@ export class Base { !!this.arraysInfo && (!val || Array.isArray(val)) ) { - if (this.isTwoValueEquals(oldValue, val)) return; - this.setArrayPropertyDirectly(name, val); + if (!this.isTwoValueEquals(oldValue, val)) { + this.setArrayPropertyDirectly(name, val); + } } else { this.setPropertyValueDirectly(name, val); if (!this.isDisposedValue && !this.isTwoValueEquals(oldValue, val)) { diff --git a/src/question_baseselect.ts b/src/question_baseselect.ts index 7e092623fe..d7934484aa 100644 --- a/src/question_baseselect.ts +++ b/src/question_baseselect.ts @@ -864,15 +864,18 @@ export class QuestionSelectBase extends Question { } return res; } - protected updateVisibleChoices() { - if (this.isLoadingFromJson) return; + protected updateVisibleChoices(): void { + if (this.isLoadingFromJson || this.isDisposed) return; var newValue = new Array(); var calcValue = this.calcVisibleChoices(); if (!calcValue) calcValue = []; for (var i = 0; i < calcValue.length; i++) { newValue.push(calcValue[i]); } - this.setPropertyValue("visibleChoices", newValue); + const oldValue = this.visibleChoices; + if(!this.isTwoValueEquals(oldValue, newValue) || this.choicesLazyLoadEnabled) { + this.setArrayPropertyDirectly("visibleChoices", newValue); + } } private calcVisibleChoices(): Array { if (this.canUseFilteredChoices()) return this.getFilteredChoices();