From 8cf7fce25d7b7fef937ccaf0b53fe16386f36145 Mon Sep 17 00:00:00 2001 From: Andrew Telnov Date: Mon, 13 Jan 2025 13:54:18 +0200 Subject: [PATCH] Refactor code related to navigation --- packages/survey-core/src/base-interfaces.ts | 2 +- packages/survey-core/src/question.ts | 5 +- .../survey-core/src/question_matrixdynamic.ts | 2 +- .../survey-core/src/question_paneldynamic.ts | 34 ++++++- packages/survey-core/src/survey.ts | 5 +- .../survey-core/tests/inputPerPageTests.ts | 97 ++++++++++++++++++- 6 files changed, 127 insertions(+), 18 deletions(-) diff --git a/packages/survey-core/src/base-interfaces.ts b/packages/survey-core/src/base-interfaces.ts index ce4955071c..bddc295c1a 100644 --- a/packages/survey-core/src/base-interfaces.ts +++ b/packages/survey-core/src/base-interfaces.ts @@ -110,7 +110,7 @@ export interface ISurvey extends ITextProcessor, ISurveyErrorOwner { areInvisibleElementsShowing: boolean; currentSingleQuestion: IQuestion; isSingleVisibleInput: boolean; - singleInputUpdateElements(updateRows: boolean): void; + updateNavigationElements(): void; areEmptyElementsHidden: boolean; isLoadingFromJson: boolean; isUpdateValueTextOnTyping: boolean; diff --git a/packages/survey-core/src/question.ts b/packages/survey-core/src/question.ts index 86e4818686..2180e3e6cd 100644 --- a/packages/survey-core/src/question.ts +++ b/packages/survey-core/src/question.ts @@ -730,6 +730,7 @@ export class Question extends SurveyElement this.resetPropertyValue("showSingleInputTitle"); this.singleInputSummary?.dispose(); this.resetPropertyValue("singleInputSummary"); + this.survey?.updateNavigationElements(); } public validateSingleInput(fireCallback: boolean = true, rec: any = null): boolean { const q = this.singleInputQuestion; @@ -764,7 +765,6 @@ export class Question extends SurveyElement } else { this.resetSingleInput(); } - this.survey.singleInputUpdateElements(true); } public nextSingleInput(): boolean { return this.nextPrevSingleInput(1); @@ -2574,9 +2574,6 @@ export class Question extends SurveyElement if (this.parent) { this.parent.onQuestionValueChanged(this); } - if(this.isSingleInputActive) { - this.survey.singleInputUpdateElements(false); - } } private checkIsValueCorrect(val: any): boolean { const res = this.isValueEmpty(val, !this.allowSpaceAsAnswer) || this.isNewValueCorrect(val); diff --git a/packages/survey-core/src/question_matrixdynamic.ts b/packages/survey-core/src/question_matrixdynamic.ts index 769da8d761..b81098ea3b 100644 --- a/packages/survey-core/src/question_matrixdynamic.ts +++ b/packages/survey-core/src/question_matrixdynamic.ts @@ -758,7 +758,7 @@ export class QuestionMatrixDynamicModel extends QuestionMatrixDropdownModelBase return res; } private singInputEditRow(row: MatrixDropdownRowModelBase): void { - const qs = row.questions; + const qs = row.visibleQuestions; if(qs.length > 0) { this.setSingleInputQuestion(qs[0]); } diff --git a/packages/survey-core/src/question_paneldynamic.ts b/packages/survey-core/src/question_paneldynamic.ts index a6c5dcb917..c9d85544ed 100644 --- a/packages/survey-core/src/question_paneldynamic.ts +++ b/packages/survey-core/src/question_paneldynamic.ts @@ -32,6 +32,7 @@ import { ComputedUpdater } from "./base"; import { AdaptiveActionContainer } from "./actions/adaptive-container"; import { ITheme } from "./themes"; import { AnimationGroup, AnimationProperty, AnimationTab, IAnimationConsumer, IAnimationGroupConsumer } from "./utils/animation"; +import { QuestionSingleInputSummary, QuestionSingleInputSummaryItem } from "./questionSingleInputSummary"; export interface IQuestionPanelDynamicData { getItemIndex(item: ISurveyData): number; @@ -285,6 +286,7 @@ export class QuestionPanelDynamicModel extends Question this.createLocalizableString("panelPrevText", this, false, "pagePrevText"); this.createLocalizableString("panelNextText", this, false, "pageNextText"); this.createLocalizableString("noEntriesText", this, false, "noEntriesText"); + this.createLocalizableString("editPanelText", this, false, "editText"); this.createLocalizableString("templateTabTitle", this, true, "panelDynamicTabTextFormat"); this.createLocalizableString("tabTitlePlaceholder", this, true, "tabTitlePlaceholder"); this.registerPropertyChangedHandlers(["panelsState"], () => { @@ -1183,11 +1185,15 @@ export class QuestionPanelDynamicModel extends Question res.onGetTextCallback = (text: string): string => { const q = this.getRootSingleInputQuestion(); if(!q) return text; - if(!text) text = this.getSingleInputTitleTemplate(); - return this.getPanelByQuestion(q)?.getProcessedText(text); + return this.processSingleInputTitle(text, this.getPanelByQuestion(q)); }; return res; } + private processSingleInputTitle(text: string, panel: PanelModel): string { + if(!text) text = this.getSingleInputTitleTemplate(); + if(!panel) return text; + return panel.getProcessedText(text); + } private getSingleInputTitleTemplate(): string { return this.getLocalizationString("panelDynamicTabTextFormat"); } @@ -1225,7 +1231,29 @@ export class QuestionPanelDynamicModel extends Question } return null; } - + protected createSingleInputSummary(): QuestionSingleInputSummary { + const bntAdd = new Action({ locTitle: this.locPanelAddText, action: () => { this.addPanelUI(); } }); + const res = new QuestionSingleInputSummary(this.locNoEntriesText, bntAdd); + const items = new Array(); + this.visiblePanels.forEach((panel) => { + const locText = new LocalizableString(this, true, undefined, this.locTemplateTitle.localizationName); + locText.setJson(this.locTemplateTitle.getJson()); + locText.onGetTextCallback = (text: string): string => { + return this.processSingleInputTitle(text, panel); + }; + const bntEdit = new Action({ locTitle: this.getLocalizableString("editPanelText"), action: () => { this.singInputEditPanel(panel); } }); + const btnRemove = new Action({ locTitle: this.locPanelRemoveText, action: () => { this.removePanelUI(panel); } }); + items.push(new QuestionSingleInputSummaryItem(locText, bntEdit, btnRemove)); + }); + res.items = items; + return res; + } + private singInputEditPanel(panel: PanelModel): void { + const qs = panel.visibleQuestions; + if(qs.length > 0) { + this.setSingleInputQuestion(qs[0]); + } + } /** * Use this property to show/hide the numbers in titles in questions inside a dynamic panel. * By default the value is "off". You may set it to "onPanel" and the first question inside a dynamic panel will start with 1 or "onSurvey" to include nested questions in dymamic panels into global survey question numbering. diff --git a/packages/survey-core/src/survey.ts b/packages/survey-core/src/survey.ts index f053fc191b..b503fb8312 100644 --- a/packages/survey-core/src/survey.ts +++ b/packages/survey-core/src/survey.ts @@ -4780,10 +4780,7 @@ export class SurveyModel extends SurveyElementCore public get isLastPage(): boolean { return this.getPropertyValue("isLastPage"); } - public singleInputUpdateElements(updateRows: boolean): void { - if(updateRows) { - this.currentPage?.updateRows(); - } + public updateNavigationElements(): void { this.updateButtonsVisibility(); } private updateButtonsVisibility(): void { diff --git a/packages/survey-core/tests/inputPerPageTests.ts b/packages/survey-core/tests/inputPerPageTests.ts index 9e6d04e4e4..1cfb327fcf 100644 --- a/packages/survey-core/tests/inputPerPageTests.ts +++ b/packages/survey-core/tests/inputPerPageTests.ts @@ -711,7 +711,6 @@ QUnit.test("singleInput & nested matrix dynamic in the panel dynamic", assert => questionsOnPageMode: "inputPerPage" }); const panel = survey.getQuestionByName("panel1"); - //let matrix = panel.panels[0].getQuestionByName("matrix1"); assert.equal(panel.singleInputQuestion.name, "name", "singleInputQuestion.name, #1"); assert.equal(panel.singleInputLocTitle.textOrHtml, "Panel 1", "input loc title #1"); survey.performNext(); @@ -730,7 +729,7 @@ QUnit.test("singleInput & nested matrix dynamic in the panel dynamic", assert => assert.equal(panel.singleInputLocTitle.textOrHtml, "Panel 1", "input loc title #5"); }); -QUnit.test("singleInput & singleInputSummary", assert => { +QUnit.test("singleInput & singleInputSummary for dynamic matrix", assert => { const survey = new SurveyModel({ elements: [ { @@ -743,7 +742,7 @@ QUnit.test("singleInput & singleInputSummary", assert => { ], questionsOnPageMode: "inputPerPage" }); - const matrix = survey.getQuestionByName("matrix1"); + const matrix = survey.getQuestionByName("matrix1"); assert.equal(survey.currentSingleQuestion.name, "matrix1", "currentSingleQuestion is matrix1, #1"); assert.equal(matrix.singleInputQuestion.name, "matrix1", "singleInputQuestion.name, #1"); assert.ok(matrix.singleInputSummary, "singleInputSummary exists, #1"); @@ -766,5 +765,93 @@ QUnit.test("singleInput & singleInputSummary", assert => { assert.equal(matrix.singleInputSummary.items[0].btnEdit.locTitle.textOrHtml, "Edit", "singleInputSummary.items[0].btnEdit.locTitle, #5"); matrix.singleInputSummary.items[0].btnEdit.action(); assert.equal(matrix.singleInputQuestion.name, "col1", "singleInputQuestion.name, #6"); - assert.equal(matrix.locSingleInputRowTitle.textOrHtml, "Row 1", "locSingleInputRowTitle, #6"); -}); \ No newline at end of file + assert.equal(matrix.singleInputLocTitle.textOrHtml, "Row 1", "singleInputLocTitle, #6"); + assert.equal(survey.isShowPrevButton, false, "prev buttton, #6"); + assert.equal(survey.isShowNextButton, true, "next buttton, #6"); +}); +QUnit.test("singleInput & singleInputSummary for dynamic panel", assert => { + const survey = new SurveyModel({ + elements: [ + { + type: "paneldynamic", name: "panel1", + templateElements: [ + { type: "text", name: "q1" }, + { type: "text", name: "q2" } + ] + } + ], + questionsOnPageMode: "inputPerPage" + }); + const panel = survey.getQuestionByName("panel1"); + assert.equal(survey.currentSingleQuestion.name, "panel1", "currentSingleQuestion is matrix1, #1"); + assert.equal(panel.singleInputQuestion.name, "panel1", "singleInputQuestion.name, #1"); + assert.ok(panel.singleInputSummary, "singleInputSummary exists, #1"); + assert.equal(panel.singleInputSummary.items.length, 0, "singleInputSummary.items.length, #1"); + panel.singleInputSummary.bntAdd.action(); + assert.equal(panel.panelCount, 1, "row count, #2"); + assert.notOk(panel.singleInputSummary, "singleInputSummary exists, #2"); + survey.performNext(); + assert.notOk(panel.singleInputSummary, "singleInputSummary exists, #3"); + survey.performNext(); + assert.ok(panel.singleInputSummary, "singleInputSummary exists, #4"); + assert.equal(panel.singleInputSummary.items.length, 1, "singleInputSummary.items.length, #4"); + panel.addPanel(); + survey.performNext(); + survey.performNext(); + assert.ok(panel.singleInputSummary, "singleInputSummary exists, #5"); + assert.equal(panel.singleInputSummary.items.length, 2, "singleInputSummary.items.length, #5"); + assert.equal(panel.singleInputSummary.items[0].locText.textOrHtml, "Panel 1", "singleInputSummary.items[0].locText, #5"); + assert.equal(panel.singleInputSummary.items[1].locText.textOrHtml, "Panel 2", "singleInputSummary.items[1].locText, #5"); + assert.equal(panel.singleInputSummary.items[0].btnEdit.locTitle.textOrHtml, "Edit", "singleInputSummary.items[0].btnEdit.locTitle, #5"); + panel.singleInputSummary.items[0].btnEdit.action(); + assert.equal(panel.singleInputQuestion.name, "q1", "singleInputQuestion.name, #6"); + assert.equal(panel.singleInputLocTitle.textOrHtml, "Panel 1", "singleInputLocTitle, #6"); + assert.equal(survey.isShowPrevButton, false, "prev buttton, #6"); + assert.equal(survey.isShowNextButton, true, "next buttton, #6"); +}); +/* +QUnit.only("singleInput & nested matrix dynamic in the panel dynamic & summary", assert => { + const survey = new SurveyModel({ + elements: [ + { + type: "paneldynamic", name: "panel1", + templateElements: [ + { + type: "matrixdynamic", name: "matrix1", + columns: [ + { cellType: "text", name: "col1" }, + { cellType: "text", name: "col2" } + ] + } + ] + } + ], + questionsOnPageMode: "inputPerPage" + }); + const panel = survey.getQuestionByName("panel1"); + assert.equal(survey.currentSingleQuestion.name, "panel1", "currentSingleQuestion is matrix1, #1"); + assert.equal(panel.singleInputQuestion.name, "panel1", "singleInputQuestion.name, #1"); + assert.ok(panel.singleInputSummary, "singleInputSummary exists, #1"); + panel.addPanel(); + assert.equal(panel.singleInputQuestion.name, "matrix1", "singleInputQuestion.name, #2"); + assert.ok(panel.singleInputSummary, "singleInputSummary exists, #2"); + panel.panels[0].getQuestion("matrix1").addRow(); + assert.equal(panel.singleInputQuestion.name, "col1", "singleInputQuestion.name, #3"); + assert.equal(panel.singleInputLocTitle.textOrHtml, "Row 1", "input loc title #3"); + assert.notOk(panel.singleInputSummary, "singleInputSummary exists, #3"); + survey.performNext(); + assert.equal(panel.singleInputQuestion.name, "col2", "singleInputQuestion.name, #4"); + assert.equal(panel.singleInputLocTitle.textOrHtml, "Row 1", "input loc title #4"); + assert.notOk(panel.singleInputSummary, "singleInputSummary exists, #4"); + survey.performNext(); + assert.equal(panel.singleInputQuestion.name, "matrix1", "singleInputQuestion.name, #5"); + assert.ok(panel.singleInputSummary, "singleInputSummary exists, #5"); + assert.equal(survey.isShowPrevButton, true, "prev buttton, #5"); + assert.equal(survey.isShowNextButton, true, "next buttton, #5"); + survey.performNext(); + assert.equal(panel.singleInputQuestion.name, "panel1", "singleInputQuestion.name, #6"); + assert.ok(panel.singleInputSummary, "singleInputSummary exists, #6"); + assert.equal(survey.isShowPrevButton, true, "prev buttton, #6"); + assert.equal(survey.isShowNextButton, true, "next buttton, #6"); +}); +*/ \ No newline at end of file