From 757e3da2595b416860cb5b6bebf16ca457d8431b Mon Sep 17 00:00:00 2001 From: Andrew Telnov Date: Wed, 6 Dec 2023 18:31:38 +0200 Subject: [PATCH] Ignore disable items in select all, fix #7465, #7466 --- src/question_checkbox.ts | 78 +++++++++++++++++++------------ tests/question_baseselecttests.ts | 60 ++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 30 deletions(-) diff --git a/src/question_checkbox.ts b/src/question_checkbox.ts index 8e78b2d958..97c62498f9 100644 --- a/src/question_checkbox.ts +++ b/src/question_checkbox.ts @@ -113,17 +113,20 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase { * @see showSelectAllItem */ public get isAllSelected(): boolean { - var val = this.value; - if (!val || !Array.isArray(val)) return false; if (this.isItemSelected(this.noneItem)) return false; - var allItemCount = this.visibleChoices.length; - const order = settings.specialChoicesOrder; - if (this.hasOther) allItemCount -= order.otherItem.length; - if (this.hasNone) allItemCount -= order.noneItem.length; - if (this.hasSelectAll) allItemCount -= order.selectAllItem.length; - var selectedCount = val.length; - if (this.isOtherSelected) selectedCount--; - return selectedCount === allItemCount; + const items = this.getVisibleEnableItems(); + if(items.length === 0) return false; + const val = this.value; + if (!val || !Array.isArray(val) || val.length === 0) return false; + if(val.length < items.length) return false; + const rVal = []; + for(let i = 0; i < val.length; i ++) { + rVal.push(this.getRealValue(val[i])); + } + for(let i = 0; i < items.length; i ++) { + if(rVal.indexOf(items[i].value) < 0) return false; + } + return true; } public set isAllSelected(val: boolean) { if (val) { @@ -132,7 +135,7 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase { this.clearValue(); } } - public toggleSelectAll() { + public toggleSelectAll(): void { this.isAllSelected = !this.isAllSelected; } /** @@ -142,16 +145,10 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase { * @see clearValue */ public selectAll(): void { - var val: Array = []; - for (var i = 0; i < this.visibleChoices.length; i++) { - var item = this.visibleChoices[i]; - if ( - item === this.noneItem || - item === this.otherItem || - item === this.selectAllItem - ) - continue; - val.push(item.value); + const val: Array = []; + const items = this.getVisibleEnableItems(); + for (let i = 0; i < items.length; i++) { + val.push(items[i].value); } this.renderedValue = val; } @@ -245,7 +242,7 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase { protected onCheckForErrors( errors: Array, isOnValueChanged: boolean - ):void { + ): void { super.onCheckForErrors(errors, isOnValueChanged); if (isOnValueChanged) return; @@ -257,28 +254,49 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase { errors.push(minError); } } - + protected onVisibleChoicesChanged(): void { + super.onVisibleChoicesChanged(); + this.updateSelectAllItemProps(); + } protected onEnableItemCallBack(item: ItemValue): boolean { if (!this.shouldCheckMaxSelectedChoices()) return true; return this.isItemSelected(item); } - protected onAfterRunItemsEnableCondition() { + protected onAfterRunItemsEnableCondition(): void { + this.updateSelectAllItemProps(); if (this.maxSelectedChoices < 1) { - this.selectAllItem.setIsEnabled(true); this.otherItem.setIsEnabled(true); return; } - if (this.hasSelectAll) { - this.selectAllItem.setIsEnabled( - this.maxSelectedChoices >= this.activeChoices.length - ); - } if (this.hasOther) { this.otherItem.setIsEnabled( !this.shouldCheckMaxSelectedChoices() || this.isOtherSelected ); } } + private updateSelectAllItemProps(): void { + if (!this.hasSelectAll) return; + this.selectAllItem.setIsEnabled(this.getSelectAllEnabled()); + } + private getSelectAllEnabled(): boolean { + if (!this.hasSelectAll) return true; + const items = this.activeChoices; + let visCount = this.getVisibleEnableItems().length; + const max = this.maxSelectedChoices; + if(max > 0 && max < visCount) return false; + return visCount > 0; + } + private getVisibleEnableItems(): Array { + const res = new Array(); + const items = this.activeChoices; + for(let i = 0; i < items.length; i ++) { + const item = items[i]; + if(item.isEnabled && item.isVisible) { + res.push(item); + } + } + return res; + } private shouldCheckMaxSelectedChoices(): boolean { if (this.maxSelectedChoices < 1) return false; var val = this.value; diff --git a/tests/question_baseselecttests.ts b/tests/question_baseselecttests.ts index de295e3e37..7bf1b9288f 100644 --- a/tests/question_baseselecttests.ts +++ b/tests/question_baseselecttests.ts @@ -1500,3 +1500,63 @@ QUnit.test("Double noneItem & selectAllItem and headItems/footItems", function ( settings.specialChoicesOrder.noneItem = [1]; settings.specialChoicesOrder.otherItem = [2]; }); +QUnit.test("Select all disable/enabled", function (assert) { + const json = { elements: [ + { type: "checkbox", name: "q1", + choices: [{ value: "a", visibleIf: "{val1}=1" }, { value: "b", enableIf: "{val2}=1" }], + showSelectAllItem: true, showNoneItem: true } + ] }; + const survey = new SurveyModel(json); + const question = survey.getQuestionByName("q1"); + const selectAll = question.selectAllItem; + assert.equal(selectAll.isEnabled, false, "#1"); + assert.equal(question.isItemSelected(selectAll), false, "#1.1"); + survey.setVariable("val1", 1); + assert.equal(selectAll.isEnabled, true, "#2"); + assert.equal(question.isItemSelected(selectAll), false, "#2.1"); + survey.setVariable("val1", 2); + assert.equal(selectAll.isEnabled, false, "#3"); + assert.equal(question.isItemSelected(selectAll), false, "#3.1"); + survey.setVariable("val2", 1); + assert.equal(selectAll.isEnabled, true, "#4"); + assert.equal(question.isItemSelected(selectAll), false, "#4.1"); + survey.setVariable("val2", 2); + assert.equal(selectAll.isEnabled, false, "#5"); + assert.equal(question.isItemSelected(selectAll), false, "#5.1"); +}); +QUnit.test("question.selectAll() disable/enabled and visible", function (assert) { + const json = { elements: [ + { type: "checkbox", name: "q1", + choices: [{ value: "a", visibleIf: "{val1}=1" }, { value: "b", enableIf: "{val2}=1" }, "c"], + showSelectAllItem: true, showNoneItem: true } + ] }; + const survey = new SurveyModel(json); + const question = survey.getQuestionByName("q1"); + const selectAll = question.selectAllItem; + assert.equal(question.isItemSelected(selectAll), false, "#1.1"); + question.selectAll(); + assert.deepEqual(question.value, ["c"], "#1.2"); + assert.equal(question.isItemSelected(selectAll), true, "#1.3"); + + survey.setVariable("val1", 1); + assert.equal(question.isItemSelected(selectAll), false, "#2.1"); + question.selectAll(); + assert.deepEqual(question.value, ["a", "c"], "#2.2"); + assert.equal(question.isItemSelected(selectAll), true, "#2.3"); + + survey.setVariable("val2", 1); + assert.equal(question.isItemSelected(selectAll), false, "#3.1"); + question.selectAll(); + assert.deepEqual(question.value, ["a", "b", "c"], "#3.2"); + assert.equal(question.isItemSelected(selectAll), true, "#3.3"); +}); +QUnit.test("Select all disable/enabled showOtherItem=true", function (assert) { + const json = { elements: [ + { type: "checkbox", name: "q1", showSelectAllItem: true, showOtherItem: true } + ] }; + const survey = new SurveyModel(json); + const question = survey.getQuestionByName("q1"); + const selectAll = question.selectAllItem; + assert.equal(selectAll.isEnabled, false, "#1"); + assert.equal(question.isItemSelected(selectAll), false, "#2"); +});