From c7d1e295d3d5b3e745f9529d2197a410adfc4c1e Mon Sep 17 00:00:00 2001 From: Andrew Telnov Date: Wed, 13 Sep 2023 12:24:01 +0300 Subject: [PATCH 1/2] Copying choices from a Dynamic Panel doesn't work if two dynamic panels share data using the valueName fix #6948 --- src/base-interfaces.ts | 5 ++-- src/panel.ts | 4 ++-- src/question.ts | 5 ++-- src/question_baseselect.ts | 7 +++--- src/question_custom.ts | 12 +++++----- src/question_matrixdropdownbase.ts | 12 +++++----- src/question_paneldynamic.ts | 10 ++++---- src/survey.ts | 22 ++++++++++-------- tests/question_baseselecttests.ts | 37 ++++++++++++++++++++++++++++++ 9 files changed, 78 insertions(+), 36 deletions(-) diff --git a/src/base-interfaces.ts b/src/base-interfaces.ts index dd7831a90c..ccf963dda3 100644 --- a/src/base-interfaces.ts +++ b/src/base-interfaces.ts @@ -19,7 +19,8 @@ export interface ISurveyData { name: string, newValue: any, locNotification: any, - allowNotifyValueChanged?: boolean + allowNotifyValueChanged?: boolean, + questionName?: string ): any; getVariable(name: string): any; setVariable(name: string, newValue: any): void; @@ -283,7 +284,7 @@ export interface IElement extends IConditionRunner, ISurveyElement { getLayoutType(): string; isLayoutTypeSupported(layoutType: string): boolean; removeElement(el: IElement): boolean; - onAnyValueChanged(name: string): any; + onAnyValueChanged(name: string, questionName: string): void; updateCustomWidgets(): any; clearIncorrectValues(): any; clearErrors(): any; diff --git a/src/panel.ts b/src/panel.ts index e784cda253..d762ba1e52 100644 --- a/src/panel.ts +++ b/src/panel.ts @@ -1427,10 +1427,10 @@ export class PanelModelBase extends SurveyElement } this.runConditionCore(values, properties); } - onAnyValueChanged(name: string) { + onAnyValueChanged(name: string, questionName: string): void { var els = this.elements; for (var i = 0; i < els.length; i++) { - els[i].onAnyValueChanged(name); + els[i].onAnyValueChanged(name, questionName); } } checkBindings(valueName: string, value: any) { diff --git a/src/question.ts b/src/question.ts index d836df3771..c529a33630 100644 --- a/src/question.ts +++ b/src/question.ts @@ -2057,7 +2057,8 @@ export class Question extends SurveyElement this.getValueName(), newValue, this.getDataLocNotification(), - this.allowNotifyValueChanged + this.allowNotifyValueChanged, + this.name ); } this.isMouseDown = false; @@ -2162,7 +2163,7 @@ export class Question extends SurveyElement this.errors = []; } public clearUnusedValues(): void { } - onAnyValueChanged(name: string): void { } + onAnyValueChanged(name: string, questionName: string): void { } checkBindings(valueName: string, value: any): void { if (this.bindings.isEmpty() || !this.data) return; var props = this.bindings.getPropertiesByValueName(valueName); diff --git a/src/question_baseselect.ts b/src/question_baseselect.ts index 61f3581981..48e81db35c 100644 --- a/src/question_baseselect.ts +++ b/src/question_baseselect.ts @@ -1191,12 +1191,13 @@ export class QuestionSelectBase extends Question { this.onVisibleChoicesChanged(); super.onSurveyLoad(); } - onAnyValueChanged(name: string) { - super.onAnyValueChanged(name); + onAnyValueChanged(name: string, questionName: string): void { + super.onAnyValueChanged(name, questionName); if (name != this.getValueName()) { this.runChoicesByUrl(); } - if (!!name && name == this.choicesFromQuestion) { + const chQuestion = this.choicesFromQuestion; + if (!!name && chQuestion && (name === chQuestion || questionName === chQuestion)) { this.onVisibleChoicesChanged(); } } diff --git a/src/question_custom.ts b/src/question_custom.ts index 61c2eb8771..e8b1c2684e 100644 --- a/src/question_custom.ts +++ b/src/question_custom.ts @@ -610,10 +610,10 @@ export class QuestionCustomModel extends QuestionCustomModelBase { protected getElement(): SurveyElement { return this.contentQuestion; } - onAnyValueChanged(name: string) { - super.onAnyValueChanged(name); + onAnyValueChanged(name: string, questionName: string): void { + super.onAnyValueChanged(name, questionName); if (!!this.contentQuestion) { - this.contentQuestion.onAnyValueChanged(name); + this.contentQuestion.onAnyValueChanged(name, questionName); } } protected getQuestionByName(name: string): IQuestion { @@ -848,11 +848,11 @@ export class QuestionCompositeModel extends QuestionCustomModelBase { questions[i].clearValueIfInvisible(reason); } } - onAnyValueChanged(name: string) { - super.onAnyValueChanged(name); + onAnyValueChanged(name: string, questionName: string): void { + super.onAnyValueChanged(name, questionName); var questions = this.contentPanel.questions; for (var i = 0; i < questions.length; i++) { - questions[i].onAnyValueChanged(name); + questions[i].onAnyValueChanged(name, questionName); } } public get hasSingleInput(): boolean { return false; } diff --git a/src/question_matrixdropdownbase.ts b/src/question_matrixdropdownbase.ts index e2096a2752..5fc0b6b20b 100644 --- a/src/question_matrixdropdownbase.ts +++ b/src/question_matrixdropdownbase.ts @@ -372,10 +372,10 @@ implements ISurveyData, ISurveyImpl, ILocalizableOwner { questions[i].clearValue(); } } - public onAnyValueChanged(name: string) { + public onAnyValueChanged(name: string, questionName: string): void { var questions = this.questions; for (var i = 0; i < questions.length; i++) { - questions[i].onAnyValueChanged(name); + questions[i].onAnyValueChanged(name, questionName); } } public getDataValueCore(valuesHash: any, key: string): any { @@ -438,7 +438,7 @@ implements ISurveyData, ISurveyImpl, ILocalizableOwner { const isDeleting = newColumnValue == null && !changedQuestion || isComment && !newColumnValue && !!changedQuestion && changedQuestion.autoOtherMode; this.data.onRowChanged(this, changedName, newValue, isDeleting); - this.onAnyValueChanged(MatrixDropdownRowModelBase.RowVariableName); + this.onAnyValueChanged(MatrixDropdownRowModelBase.RowVariableName, ""); } private updateQuestionsValue( @@ -2084,7 +2084,7 @@ export class QuestionMatrixDropdownModelBase extends QuestionMatrixBaseModelsurvey.getQuestionByName("q1"); + const q2 = survey.getQuestionByName("q2"); + q1.addPanel(); + q1.panels[0].getQuestionByName("q1-q1").value = "aaa"; + q1.addPanel(); + q1.panels[1].getQuestionByName("q1-q1").value = "bbb"; + assert.equal(q2.visibleChoices.length, 2, "There are two choice"); + assert.equal(q2.visibleChoices[0].value, "aaa", "the first value is correct"); + assert.equal(q2.visibleChoices[1].value, "bbb", "the second value is correct"); +}); +QUnit.test("Use carryForward with panel dynamic + choiceValuesFromQuestion + valueName, Bug#6948-2", function (assert) { + const survey = new SurveyModel({ elements: [ + { type: "paneldynamic", name: "q1", valueName: "sharedData", + templateElements: [{ name: "q1-q1", type: "text" }] + }, + { type: "paneldynamic", name: "q2", valueName: "sharedData", + templateElements: [{ name: "q2-q2", type: "checkbox", choicesFromQuestion: "q1", choiceValuesFromQuestion: "q1-q1" }] + } + ] }); + const q1 = survey.getQuestionByName("q1"); + const q2 = survey.getQuestionByName("q2"); + q1.addPanel(); + q1.panels[0].getQuestionByName("q1-q1").value = "aaa"; + q1.addPanel(); + q1.panels[1].getQuestionByName("q1-q1").value = "bbb"; + const q2_q2 = q2.panels[0].getQuestionByName("q2-q2"); + assert.equal(q2_q2.visibleChoices.length, 2, "There are two choice"); + assert.equal(q2_q2.visibleChoices[0].value, "aaa", "the first value is correct"); + assert.equal(q2_q2.visibleChoices[1].value, "bbb", "the second value is correct"); +}); From fafeb9b8bcfef099bfb4af298ec504a4b83cdc18 Mon Sep 17 00:00:00 2001 From: Andrew Telnov Date: Wed, 13 Sep 2023 12:54:04 +0300 Subject: [PATCH 2/2] Add explicitErrorHandler into a functional test --- testCafe/survey/titleActions.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testCafe/survey/titleActions.js b/testCafe/survey/titleActions.js index 682b35ddc7..902e881728 100644 --- a/testCafe/survey/titleActions.js +++ b/testCafe/survey/titleActions.js @@ -1,4 +1,4 @@ -import { frameworks, url, initSurvey, url_test, applyTheme } from "../helper"; +import { frameworks, url, initSurvey, url_test, applyTheme, explicitErrorHandler } from "../helper"; import { Selector, ClientFunction, fixture, test } from "testcafe"; const title = "titleActions"; @@ -320,6 +320,7 @@ frameworks.forEach((framework) => { await applyTheme(themeName); }); test("check hidden action content has non-zero width", async (t) => { + await explicitErrorHandler(); await initSurvey(framework, json, { onGetQuestionTitleActions: (_, opt) => { opt.titleActions = [