From 96ea02f0ab5680129b7b361d50bc979580135964 Mon Sep 17 00:00:00 2001 From: Andrew Telnov Date: Fri, 31 May 2024 13:08:21 +0300 Subject: [PATCH] Do not expand panel on checking errors if there is no errors inside it fix #8341 --- src/panel.ts | 40 +++++++++------- src/question.ts | 11 +++-- src/question_paneldynamic.ts | 2 +- src/survey.ts | 2 +- tests/paneltests.ts | 89 +++++++++++++++++++++++++++++++++--- 5 files changed, 116 insertions(+), 28 deletions(-) diff --git a/src/panel.ts b/src/panel.ts index b3842d75b7..4621ed923f 100644 --- a/src/panel.ts +++ b/src/panel.ts @@ -854,15 +854,12 @@ export class PanelModelBase extends SurveyElement ? rec : { fireCallback: fireCallback, - focuseOnFirstError: focusOnFirstError, + focusOnFirstError: focusOnFirstError, firstErrorQuestion: null, result: false, }; if(rec.result !== true) rec.result = false; this.hasErrorsCore(rec); - if (rec.focuseOnFirstError && rec.firstErrorQuestion) { - rec.firstErrorQuestion.focus(true); - } return !rec.result; } public validateContainerOnly(): void { @@ -871,7 +868,7 @@ export class PanelModelBase extends SurveyElement this.parent.validateContainerOnly(); } } - private hasErrorsInPanels(rec: any) { + private hasErrorsInPanels(rec: any): void { var errors = >[]; this.hasRequiredError(rec, errors); if (this.survey) { @@ -894,7 +891,7 @@ export class PanelModelBase extends SurveyElement return text; } - private hasRequiredError(rec: any, errors: Array) { + private hasRequiredError(rec: any, errors: Array): void { if (!this.isRequired) return; var visQuestions = >[]; this.addQuestionsToList(visQuestions, true); @@ -904,13 +901,14 @@ export class PanelModelBase extends SurveyElement } rec.result = true; errors.push(new OneAnswerRequiredError(this.requiredErrorText, this)); - if (rec.focuseOnFirstError && !rec.firstErrorQuestion) { + if (rec.focusOnFirstError && !rec.firstErrorQuestion) { rec.firstErrorQuestion = visQuestions[0]; } } - protected hasErrorsCore(rec: any) { - var elements = this.elements; - var element = null; + protected hasErrorsCore(rec: any): void { + const elements = this.elements; + let element = null; + let firstErroredEl = null; for (var i = 0; i < elements.length; i++) { element = elements[i]; @@ -921,6 +919,9 @@ export class PanelModelBase extends SurveyElement } else { var question = element; if (!question.validate(rec.fireCallback, rec)) { + if(!firstErroredEl) { + firstErroredEl = question; + } if (!rec.firstErrorQuestion) { rec.firstErrorQuestion = question; } @@ -930,6 +931,19 @@ export class PanelModelBase extends SurveyElement } this.hasErrorsInPanels(rec); this.updateContainsErrors(); + if(!firstErroredEl && this.errors.length > 0) { + firstErroredEl = this.getFirstQuestionToFocus(false, true); + if(!rec.firstErrorQuestion) { + rec.firstErrorQuestion = firstErroredEl; + } + } + if(rec.fireCallback && firstErroredEl) { + if(firstErroredEl === rec.firstErrorQuestion && rec.focusOnFirstError) { + firstErroredEl.focus(true); + } else { + firstErroredEl.expandAllParents(); + } + } } protected getContainsErrors(): boolean { var res = super.getContainsErrors(); @@ -1939,12 +1953,6 @@ export class PanelModel extends PanelModelBase implements IElement { this.survey.panelVisibilityChanged(this, this.isVisible); } } - protected hasErrorsCore(rec: any) { - super.hasErrorsCore(rec); - if (this.isCollapsed && rec.result && rec.fireCallback) { - this.expand(); - } - } protected getRenderedTitle(str: string): string { if (!str) { if (this.isCollapsed || this.isExpanded) return this.name; diff --git a/src/question.ts b/src/question.ts index ec09f98648..05729bb8b8 100644 --- a/src/question.ts +++ b/src/question.ts @@ -1194,7 +1194,7 @@ export class Question extends SurveyElement } private focuscore(onError: boolean = false, scrollIfVisible?: boolean): void { if (!!this.survey) { - this.expandAllParents(this); + this.expandAllParents(); this.survey.scrollElementToTop(this, this, null, this.id, scrollIfVisible); } var id = !onError @@ -1204,13 +1204,16 @@ export class Question extends SurveyElement this.fireCallback(this.focusCallback); } } - private expandAllParents(element: IElement) { + public expandAllParents(): void { + this.expandAllParentsCore(this); + } + private expandAllParentsCore(element: IElement) { if (!element) return; if (element.isCollapsed) { element.expand(); } - this.expandAllParents((element).parent); - this.expandAllParents((element).parentQuestion); + this.expandAllParentsCore((element).parent); + this.expandAllParentsCore((element).parentQuestion); } public focusIn(): void { if (!this.survey || this.isDisposed || this.isContainer) return; diff --git a/src/question_paneldynamic.ts b/src/question_paneldynamic.ts index e9f2bd1fa7..68ec2c0700 100644 --- a/src/question_paneldynamic.ts +++ b/src/question_paneldynamic.ts @@ -1933,7 +1933,7 @@ export class QuestionPanelDynamicModel extends Question for (var i = 0; i < panels.length; i++) { var pnlError = panels[i].hasErrors( fireCallback, - !!rec && rec.focuseOnFirstError, + !!rec && rec.focusOnFirstError, rec ); pnlError = this.isValueDuplicated(panels[i], keyValues, rec, fireCallback) || pnlError; diff --git a/src/survey.ts b/src/survey.ts index e117a63600..76f8f69999 100644 --- a/src/survey.ts +++ b/src/survey.ts @@ -3875,7 +3875,7 @@ export class SurveyModel extends SurveyElementCore } var visPages = this.visiblePages; var res = true; - const rec = { fireCallback: fireCallback, focuseOnFirstError: focusOnFirstError, firstErrorQuestion: null, result: false }; + const rec = { fireCallback: fireCallback, focusOnFirstError: focusOnFirstError, firstErrorQuestion: null, result: false }; for (var i = 0; i < visPages.length; i++) { if (!visPages[i].validate(fireCallback, focusOnFirstError, rec)) { res = false; diff --git a/tests/paneltests.ts b/tests/paneltests.ts index dfe9546c75..bf4350604c 100644 --- a/tests/paneltests.ts +++ b/tests/paneltests.ts @@ -328,7 +328,7 @@ QUnit.test("Panel with paneldynamic error focus", function (assert) { }; const survey = new SurveyModel(json); const rec = { - focuseOnFirstError: true, + focusOnFirstError: true, firstErrorQuestion: null, }; const panel = survey.getPanelByName("p1"); @@ -373,16 +373,16 @@ QUnit.test("Required panel error focus/not focus - T3101 - Stop focus when page const survey = new SurveyModel(json); const page = survey.currentPage; - const rec = { - focuseOnFirstError: true, - firstErrorQuestion: null, + const rec: any = { + fireCallback: true, + focusOnFirstError: true }; page.hasErrors(true, true, rec); assert.equal(rec.firstErrorQuestion.name, "chk1", "scroll to first question in the dynamicpanel instead of dynamicpanel itself"); - assert.equal(rec.firstErrorQuestion.inputId, focusedQuestionId, "focus the question"); + assert.equal(focusedQuestionId, rec.firstErrorQuestion.inputId, "focus the question"); focusedQuestionId = ""; - rec.focuseOnFirstError = false; + rec.focusOnFirstError = false; rec.firstErrorQuestion = null; page.hasErrors(true, false, rec); assert.notOk(focusedQuestionId, "don't scroll to question - T3101 - Stop focus when page has error"); @@ -2707,4 +2707,81 @@ QUnit.test("Check insert function: insert between rows", (assert) => { assert.deepEqual(page.rows[6].visibleElements.map(q => q.name), ["q5", "q6"]); assert.equal(calledBuildRows, 0); +}); +QUnit.test("Do not expand panels on validation that doesn't have an error Bug#8341", function (assert) { + const survey = new SurveyModel({ + "elements": [ + { + "type": "text", + "name": "question1", + "isRequired": true + }, + { + "type": "panel", + "name": "panel1", + "elements": [ + { + "type": "text", + "name": "question2" + } + ], + "title": "Title", + "state": "collapsed" + }, + { + "type": "panel", + "name": "panel2", + "elements": [ + { + "type": "text", + "name": "question3", + "isRequired": true + } + ], + "title": "Title", + "state": "collapsed" + }, + { + "type": "panel", + "name": "panel3", + "elements": [ + { + "type": "panel", + "name": "panel4", + "elements": [ + { + "type": "text", + "name": "question4", + "isRequired": true + } + ], + "title": "Title", + "state": "collapsed" + } + ], + "title": "Title", + "state": "collapsed" + }, + { + "type": "panel", + "name": "panel5", + "isRequired": true, + "elements": [ + { + "type": "text", + "name": "question2" + } + ], + "title": "Title", + "state": "collapsed" + }, + ] + }); + survey.validate(true, true); + const panels = survey.getAllPanels(); + assert.equal(panels[0].isCollapsed, true, "The panel should rename collapsed"); + assert.equal(panels[1].isExpanded, true, "The panel should be expanded, it has error inside, #1"); + assert.equal(panels[2].isExpanded, true, "The panel should be expanded, it has error inside, #2"); + assert.equal(panels[3].isExpanded, true, "The panel should be expanded, it has error inside, #3"); + assert.equal(panels[4].isExpanded, true, "The panel should be expanded, panel is required, #4"); }); \ No newline at end of file