Skip to content

Commit

Permalink
Features/6614 question errorlocation (#6757)
Browse files Browse the repository at this point in the history
* Add question.errorLocation property #6614

* Add questionErrorLocation into IPanel #6614

* Implement cellsErrorLocation&detailErrorLocation

* Refactor unit tests #6614

* itemErrorLocation #6614

* Remove visible: false attributes from error location props #6614

* Add descriptions

* Rename cellsErrorLocation to cellErrorLocation #6614

---------

Co-authored-by: Roman Tsukanov <[email protected]>
  • Loading branch information
andrewtelnov and Roman Tsukanov authored Aug 22, 2023
1 parent bc62dcd commit 7e9f566
Show file tree
Hide file tree
Showing 13 changed files with 291 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/base-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ export interface IPanel extends ISurveyElement, IParentElement {
getChildrenLayoutType(): string;
getQuestionTitleLocation(): string;
getQuestionStartIndex(): string;
getQuestionErrorLocation(): string;
parent: IPanel;
elementWidthChanged(el: IElement): any;
indexOf(el: IElement): number;
Expand Down
25 changes: 23 additions & 2 deletions src/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1471,7 +1471,28 @@ export class PanelModelBase extends SurveyElement<Question>
public get cssDescription(): string {
return this.cssClasses.panel.description;
}

/**
* Specifies the error message position for questions that belong to this page/panel.
*
* Use this property to override the [`questionErrorLocation`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#questionErrorLocation) property specified for the survey. You can also set the [`errorLocation`](https://surveyjs.io/form-library/documentation/question#errorLocation) property for individual questions.
*
* Possible values:
*
* - `"default"` (default) - Inherits the setting from the `questionErrorLocation` property specified for the survey.
* - `"top"` - Displays error messages above questions.
* - `"bottom"` - Displays error messages below questions.
*/
public get questionErrorLocation(): string {
return this.getPropertyValue("questionErrorLocation");
}
public set questionErrorLocation(val: string) {
this.setPropertyValue("questionErrorLocation", val);
}
public getQuestionErrorLocation(): string {
if(this.questionErrorLocation !== "default") return this.questionErrorLocation;
if(this.parent) return this.parent.getQuestionErrorLocation();
return this.survey ? this.survey.questionErrorLocation : "top";
}
//ITitleOwner
public get no(): string { return ""; }
public dispose() {
Expand Down Expand Up @@ -1855,7 +1876,7 @@ Serializer.addClass(
default: "default",
choices: ["default", "initial", "random"],
},

{ name: "questionErrorLocation", default: "default", choices: ["default", "top", "bottom"] }
],
function () {
return new PanelModelBase();
Expand Down
28 changes: 25 additions & 3 deletions src/question.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,9 +566,30 @@ export class Question extends SurveyElement<Question>
const location = this.getTitleLocation();
return location === "left" || location === "top";
}
/**
* Specifies the error message position. Overrides the `questionErrorLocation` property specified for the question's container ([survey](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#questionErrorLocation), [page](https://surveyjs.io/form-library/documentation/api-reference/page-model#questionErrorLocation), or [panel](https://surveyjs.io/form-library/documentation/api-reference/panel-model#questionErrorLocation)).
*
* Possible values:
*
* - `"default"` (default) - Inherits the setting from the `questionErrorLocation` property specified for the question's container.
* - `"top"` - Displays error messages above questions.
* - `"bottom"` - Displays error messages below questions.
*/
public get errorLocation(): string {
return this.getPropertyValue("errorLocation");
}
public set errorLocation(val: string) {
this.setPropertyValue("errorLocation", val);
}
public getErrorLocation(): string {
if(this.errorLocation !== "default") return this.errorLocation;
if(this.parentQuestion) return this.parentQuestion.getChildErrorLocation(this);
if(this.parent) return this.parent.getQuestionErrorLocation();
return this.survey ? this.survey.questionErrorLocation : "top";
}
public getChildErrorLocation(child: Question): string {
return this.getErrorLocation();
}
/**
* Returns `false` if the question has no input fields ([HTML](https://surveyjs.io/form-library/documentation/questionhtmlmodel), [Image](https://surveyjs.io/form-library/documentation/questionimagemodel), and similar question types).
* @see hasSingleInput
Expand Down Expand Up @@ -871,7 +892,7 @@ export class Question extends SurveyElement<Question>
}

public showErrorOnCore(location: string): boolean {
return !this.isErrorsModeTooltip && !this.showErrorsAboveQuestion && !this.showErrorsBelowQuestion && this.errorLocation === location;
return !this.isErrorsModeTooltip && !this.showErrorsAboveQuestion && !this.showErrorsBelowQuestion && this.getErrorLocation() === location;
}

public get showErrorOnTop(): boolean {
Expand All @@ -891,10 +912,10 @@ export class Question extends SurveyElement<Question>
return this.isDefaultV2Theme && !(this.hasParent && this.getIsTooltipErrorSupportedByParent());
}
public get showErrorsAboveQuestion(): boolean {
return this.showErrorsOutsideQuestion && this.errorLocation === "top";
return this.showErrorsOutsideQuestion && this.getErrorLocation() === "top";
}
public get showErrorsBelowQuestion(): boolean {
return this.showErrorsOutsideQuestion && this.errorLocation === "bottom";
return this.showErrorsOutsideQuestion && this.getErrorLocation() === "bottom";
}

public get cssError(): string {
Expand Down Expand Up @@ -2396,6 +2417,7 @@ Serializer.addClass("question", [
name: "requiredErrorText:text",
serializationProperty: "locRequiredErrorText",
},
{ name: "errorLocation", default: "default", choices: ["default", "top", "bottom"] },
{ name: "readOnly:switch", overridingProperty: "enableIf" },
{
name: "validators:validators",
Expand Down
3 changes: 3 additions & 0 deletions src/question_custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,9 @@ export abstract class QuestionCustomModelBase extends Question
validateContainerOnly(): void {
// do nothing
}
getQuestionErrorLocation(): string {
return this.getErrorLocation();
}
protected getContentDisplayValueCore(keyAsText: boolean, value: any, question: Question): any {
if (!question) return super.getDisplayValueCore(keyAsText, value);
return this.customQuestion.getDisplayValue(keyAsText, value, question);
Expand Down
42 changes: 42 additions & 0 deletions src/question_matrixdropdownbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,43 @@ export class QuestionMatrixDropdownModelBase extends QuestionMatrixBaseModel<Mat
set columnsLocation(val: string) {
this.columnLayout = val;
}
/**
* Specifies the error message position for question within detail sections.
*
* Possible values:
*
* - `"default"` (default) - Inherits the setting from the [`errorLocation`](#errorLocation) property.
* - `"top"` - Displays error messages above questions.
* - `"bottom"` - Displays error messages below questions.
* @see cellErrorLocation
*/
public get detailErrorLocation(): string {
return this.getPropertyValue("detailErrorLocation");
}
public set detailErrorLocation(value: string) {
this.setPropertyValue("detailErrorLocation", value.toLowerCase());
}
/**
* Specifies the error message position relative to matrix cells.
*
* Possible values:
*
* - `"default"` (default) - Inherits the setting from the [`errorLocation`](#errorLocation) property.
* - `"top"` - Displays error messages above matrix cells.
* - `"bottom"` - Displays error messages below matrix cells.
* @see detailErrorLocation
*/
public get cellErrorLocation(): string {
return this.getPropertyValue("cellErrorLocation");
}
public set cellErrorLocation(value: string) {
this.setPropertyValue("cellErrorLocation", value.toLowerCase());
}
public getChildErrorLocation(child: Question): string {
const errLocation = !!child.parent ? this.detailErrorLocation : this.cellErrorLocation;
if(errLocation !== "default") return errLocation;
return super.getChildErrorLocation(child);
}
/**
* Returns `true` if columns are placed in the horizontal direction and rows in the vertical direction.
*
Expand Down Expand Up @@ -2264,6 +2301,7 @@ export class QuestionMatrixDropdownModelBase extends QuestionMatrixBaseModel<Mat
if (!!this.onCreateDetailPanelCallback) {
this.onCreateDetailPanelCallback(row, panel);
}
panel.questions.forEach(q => q.setParentQuestion(this));
return panel;
}
getSharedQuestionByName(
Expand Down Expand Up @@ -2383,6 +2421,10 @@ Serializer.addClass(
choices: ["none", "underRow", "underRowSingle"],
default: "none",
},
{ name: "cellErrorLocation", default: "default", choices: ["default", "top", "bottom"] },
{ name: "detailErrorLocation", default: "default", choices: ["default", "top", "bottom"],
visibleIf: (obj: any) => { return !!obj && obj.detailPanelMode != "none"; }
},
"horizontalScroll:boolean",
{
name: "choices:itemvalue[]", uniqueProperty: "value",
Expand Down
4 changes: 2 additions & 2 deletions src/question_matrixdropdownrendered.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,11 @@ export class QuestionMatrixDropdownRenderedTable extends Base {

private get showCellErrorsTop() {
//todo
return this.matrix.errorLocation == "top";
return this.matrix.getErrorLocation() === "top";
}
private get showCellErrorsBottom() {
//todo
return this.matrix.errorLocation == "bottom";
return this.matrix.getErrorLocation() === "bottom";
}

protected build() {
Expand Down
24 changes: 24 additions & 0 deletions src/question_multipletext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class MultipleTextItemModel extends Base
this.editor.defaultValue = data.getItemDefaultValue(this.name);
this.editor.setSurveyImpl(this);
this.editor.parent = data;
this.editor.setParentQuestion(<any>data);
}
}
/**
Expand Down Expand Up @@ -448,6 +449,28 @@ export class QuestionMultipleTextModel extends Question
this.items[i].localeChanged();
}
}
/**
* Specifies the error message position relative to individual input fields.
*
* Possible values:
*
* - `"default"` (default) - Inherits the setting from the [`errorLocation`](#errorLocation) property.
* - `"top"` - Displays error messages above input fields.
* - `"bottom"` - Displays error messages below input fields.
*/
public get itemErrorLocation(): string {
return this.getPropertyValue("itemErrorLocation");
}
public set itemErrorLocation(val: string) {
this.setPropertyValue("itemErrorLocation", val);
}
public getQuestionErrorLocation(): string {
if(this.itemErrorLocation !== "default") return this.itemErrorLocation;
return this.getErrorLocation();
}
public getChildErrorLocation(child: Question): string {
return this.getQuestionErrorLocation();
}
protected isNewValueCorrect(val: any): boolean {
return Helpers.isValueObject(val);
}
Expand Down Expand Up @@ -699,6 +722,7 @@ Serializer.addClass(
{ name: "!items:textitems", className: "multipletextitem" },
{ name: "itemSize:number", minValue: 0 },
{ name: "colCount:number", default: 1, choices: [1, 2, 3, 4, 5] },
{ name: "itemErrorLocation", default: "default", choices: ["default", "top", "bottom"], visible: false }
],
function () {
return new QuestionMultipleTextModel("");
Expand Down
27 changes: 22 additions & 5 deletions src/question_paneldynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,21 @@ export class QuestionPanelDynamicModel extends Question
public set templateTitleLocation(value: string) {
this.setPropertyValue("templateTitleLocation", value.toLowerCase());
}
/**
* Specifies the error message position.
*
* Possible values:
*
* - `"default"` (default) - Inherits the setting from the [`errorLocation`](#errorLocation) property.
* - `"top"` - Displays error messages above questions.
* - `"bottom"` - Displays error messages below questions.
*/
public get templateErrorLocation(): string {
return this.getPropertyValue("templateErrorLocation");
}
public set templateErrorLocation(value: string) {
this.setPropertyValue("templateErrorLocation", value.toLowerCase());
}
/**
* 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.
Expand Down Expand Up @@ -1765,17 +1780,18 @@ export class QuestionPanelDynamicModel extends Question
var panel = this.createNewPanelObject();
panel.isInteractiveDesignElement = false;
panel.setParentQuestion(this);
var self = this;
panel.onGetQuestionTitleLocation = function () {
return self.getTemplateQuestionTitleLocation();
};
panel.onGetQuestionTitleLocation = () => this.getTemplateQuestionTitleLocation();
return panel;
}
private getTemplateQuestionTitleLocation() {
private getTemplateQuestionTitleLocation(): string {
return this.templateTitleLocation != "default"
? this.templateTitleLocation
: this.getTitleLocationCore();
}
public getChildErrorLocation(child: Question): string {
if(this.templateErrorLocation !== "default") return this.templateErrorLocation;
return super.getChildErrorLocation(child);
}
protected createNewPanelObject(): PanelModel {
return Serializer.createClass("panel");
}
Expand Down Expand Up @@ -2300,6 +2316,7 @@ Serializer.addClass(
default: "default",
choices: ["default", "top", "bottom", "left"],
},
{ name: "templateErrorLocation", default: "default", choices: ["default", "top", "bottom"] },
{
name: "templateVisibleIf:expression",
category: "logic"
Expand Down
10 changes: 6 additions & 4 deletions src/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2532,12 +2532,14 @@ export class SurveyModel extends SurveyElementCore
this.updateCss();
}
/**
* Gets or sets the error message position.
* Specifies the error message position.
*
* The following options are available:
* Possible values:
*
* - `"top"` (default) - Displays error messages above questions.
* - `"bottom"` - Displays error messages below questions.
*
* - `top` - to show question error(s) over the question,
* - `bottom` - to show question error(s) under the question.
* You can override this setting if you specify the `questionErrorLocation` property for an [individual page](https://surveyjs.io/form-library/documentation/pagemodel#questionErrorLocation) or [panel](https://surveyjs.io/form-library/documentation/panelmodel#questionErrorLocation) or set the `errorLocation` property for a [specific question](https://surveyjs.io/form-library/documentation/question#errorLocation).
*/
public get questionErrorLocation(): string {
return this.getPropertyValue("questionErrorLocation");
Expand Down
34 changes: 34 additions & 0 deletions tests/question_matrixdropdownbasetests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,3 +579,37 @@ QUnit.test("Column width is not loaded, bug in Creator #4303", function (assert)
assert.equal(matrix.columns.length, 1, "There is one column");
assert.equal(matrix.columns[0].width, "222px", "column width is loaded correctly");
});
QUnit.test("Error location from survey/matrix/properties", function (assert) {
const survey = new SurveyModel({
questionErrorLocation: "bottom",
elements: [
{
type: "matrixdropdown",
name: "matrix",
columns: [{ name: "col1" }],
rows: [0],
detailPanelMode: "underRow",
detailElements: [{ type: "text", name: "q1" }],
},
],
});
const matrix = <QuestionMatrixDropdownModelBase>survey.getQuestionByName("matrix");
const row = matrix.visibleRows[0];
row.showDetailPanel();
const qCell = row.cells[0].question;
const qDetail = row.detailPanel.getQuestionByName("q1");
assert.ok(qCell, "qCell");
assert.ok(qDetail, "qDetail");
assert.equal(qCell.getErrorLocation(), "bottom", "cell, #1");
assert.equal(qDetail.getErrorLocation(), "bottom", "question in detail panel, #1");
matrix.errorLocation = "top";
assert.equal(qCell.getErrorLocation(), "top", "cell, #2");
assert.equal(qDetail.getErrorLocation(), "top", "question in detail panel, #2");
matrix.cellErrorLocation = "bottom";
assert.equal(qCell.getErrorLocation(), "bottom", "cell, #3");
assert.equal(qDetail.getErrorLocation(), "top", "question in detail panel, #3");
matrix.cellErrorLocation = "default";
matrix.detailErrorLocation = "bottom";
assert.equal(qCell.getErrorLocation(), "top", "cell, #4");
assert.equal(qDetail.getErrorLocation(), "bottom", "question in detail panel, #4");
});
Loading

0 comments on commit 7e9f566

Please sign in to comment.