Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitry-kurmanov committed Jul 27, 2023
1 parent 2c21bca commit 322000e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 12 deletions.
43 changes: 43 additions & 0 deletions src/question_checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { surveyLocalization } from "./surveyStrings";
import { LocalizableString } from "./localizablestring";
import { CssClassBuilder } from "./utils/cssClassBuilder";
import { IQuestion } from "./base-interfaces";
import { SurveyError } from "./survey-error";
import { CustomError } from "./error";

/**
* A class that describes the Checkbox question type.
Expand Down Expand Up @@ -182,6 +184,20 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
this.setPropertyValue("maxSelectedChoices", val);
this.filterItems();
}
/**
* Sets a limit on the number of selected choices.
*
* Default value: 0 (unlimited)
*
* > This property only limits the number of choice items that can be selected by users. You can select any number of choice items in code, regardless of the `maxSelectedChoices` value.
*/
public get minSelectedChoices(): number {
return this.getPropertyValue("minSelectedChoices");
}
public set minSelectedChoices(val: number) {
if (val < 0) val = 0;
this.setPropertyValue("minSelectedChoices", val);
}
/**
* An array of selected choice items. Includes the "Other" and "None" choice items if they are selected, but not "Select All". Items are sorted in the order they were selected.
* @see visibleChoices
Expand Down Expand Up @@ -215,6 +231,23 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
const val = this.renderedValue as Array<any>;
return val.map((item: any) => new ItemValue(item));
}

protected onCheckForErrors(
errors: Array<SurveyError>,
isOnValueChanged: boolean
) {
super.onCheckForErrors(errors, isOnValueChanged);
if (isOnValueChanged) return;

if (this.minSelectedChoices > 0 && this.checkMinSelectedChoicesUnreached()) {
const minError = new CustomError(
this.getLocalizationFormatString("minSelectError", this.minSelectedChoices),
this
);
errors.push(minError);
}
}

protected onEnableItemCallBack(item: ItemValue): boolean {
if (!this.shouldCheckMaxSelectedChoices()) return true;
return this.isItemSelected(item);
Expand Down Expand Up @@ -242,6 +275,14 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
var len = !Array.isArray(val) ? 0 : val.length;
return len >= this.maxSelectedChoices;
}

private checkMinSelectedChoicesUnreached(): boolean {
if (this.minSelectedChoices < 1) return false;
var val = this.value;
var len = !Array.isArray(val) ? 0 : val.length;
return len < this.minSelectedChoices;
}

protected getItemClassCore(item: any, options: any) {
const __dummy_value = this.value; //trigger dependencies from koValue for knockout
options.isSelectAllItem = item === this.selectAllItem;
Expand Down Expand Up @@ -439,6 +480,7 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
json["type"] = "radiogroup";
}
json["maxSelectedChoices"] = 0;
json["minSelectedChoices"] = 0;
return json;
}
public isAnswerCorrect(): boolean {
Expand Down Expand Up @@ -537,6 +579,7 @@ Serializer.addClass(
{ name: "showSelectAllItem:boolean", alternativeName: "hasSelectAll" },
{ name: "separateSpecialChoices", visible: true },
{ name: "maxSelectedChoices:number", default: 0 },
{ name: "minSelectedChoices:number", default: 0 },
{
name: "selectAllText",
serializationProperty: "locSelectAllText",
Expand Down
1 change: 1 addition & 0 deletions src/question_ranking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ Serializer.addClass(
{ name: "selectAllText", visible: false, isSerializable: false },
{ name: "colCount:number", visible: false, isSerializable: false },
{ name: "maxSelectedChoices", visible: false, isSerializable: false },
{ name: "minSelectedChoices", visible: false, isSerializable: false },
{ name: "separateSpecialChoices", visible: false, isSerializable: false },
{
name: "longTap",
Expand Down
28 changes: 17 additions & 11 deletions src/question_text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { QuestionFactory } from "./questionfactory";
import { Serializer } from "./jsonobject";
import { LocalizableString, LocalizableStrings } from "./localizablestring";
import { Helpers, HashTable } from "./helpers";
import { EmailValidator, SurveyValidator } from "./validator";
import { EmailValidator } from "./validator";
import { SurveyError } from "./survey-error";
import { CustomError } from "./error";
import { settings } from "./settings";
Expand Down Expand Up @@ -71,16 +71,7 @@ export class QuestionTextModel extends QuestionTextBase {
this.setRenderedMinMax(values, properties);
}
}
public getValidators(): Array<SurveyValidator> {
var validators = super.getValidators();
if (
this.inputType === "email" &&
!this.validators.some((v) => v.getType() === "emailvalidator")
) {
validators.push(new EmailValidator());
}
return validators;
}

isLayoutTypeSupported(layoutType: string): boolean {
return true;
}
Expand Down Expand Up @@ -257,7 +248,22 @@ export class QuestionTextModel extends QuestionTextBase {
); };
errors.push(maxError);
}

var name = this.name;
var emailValidator = new EmailValidator();
if (
this.inputType === "email" &&
!this.validators.some((v) => v.getType() === "emailvalidator") &&
emailValidator.validate(this.value, name)
) {
const maxError = new CustomError(
emailValidator.getErrorText(name),
this
);
errors.push(maxError);
}
}

protected canSetValueToSurvey(): boolean {
if (!this.isMinMaxType) return true;
const isValid = !this.isValueLessMin && !this.isValueGreaterMax;
Expand Down
2 changes: 1 addition & 1 deletion src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class SurveyValidator extends Base {
get locText(): LocalizableString {
return this.getLocalizableString("text");
}
protected getErrorText(name: string): string {
public getErrorText(name: string): string {
if (this.text) return this.text;
return this.getDefaultErrorText(name);
}
Expand Down
23 changes: 23 additions & 0 deletions tests/surveyquestiontests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5116,6 +5116,29 @@ QUnit.test("select items and then set maxSelectedChoices in checkbox", function
assert.equal(question.otherItem.isEnabled, false, "otherItem is disabled");
});

QUnit.test("select items and then set minSelectedChoices in checkbox", function (assert) {
var survey = new SurveyModel({
elements: [
{
type: "checkbox",
name: "q1",
choices: [1, 2, 3, 4, 5],
hasSelectAll: true,
hasOther: true,
},
],
});
var question = <QuestionCheckboxModel>survey.getQuestionByName("q1");
question.minSelectedChoices = 3;
question.value = [2, 3];
question.validate();
assert.equal(question.hasErrors(), true, "has errors");

question.value = [2, 3, 4];
question.validate();
assert.equal(question.hasErrors(), false, "has no errors");
});

QUnit.test("Matrix Question: columns with true/false values", function (assert) {
var matrix = new QuestionMatrixModel("q1");
matrix.columns = [true, false, 0, "0", 1];
Expand Down

0 comments on commit 322000e

Please sign in to comment.