Skip to content

Commit

Permalink
Implement functionality for refuse and don't know items #7754
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewtelnov committed Jan 29, 2024
1 parent b7175c5 commit c7ae838
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 100 deletions.
2 changes: 1 addition & 1 deletion src/localization/english.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export var englishStrings = {
otherItemText: "Other (describe)",
noneItemText: "None",
refuseItemText: "Refuse to answer",
donotKnowItemText: "Do not know",
dontKnowItemText: "Don't know",
selectAllItemText: "Select All",
progressText: "Page {0} of {1}",
indexText: "{0} of {1}",
Expand Down
98 changes: 59 additions & 39 deletions src/question_baseselect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class QuestionSelectBase extends Question {
private enableOnLoadingChoices: boolean;
private noneItemValue: ItemValue;
private refuseItemValue: ItemValue;
private donotKnowItemValue: ItemValue;
private dontKnowItemValue: ItemValue;
private newItemValue: ItemValue;
private canShowOptionItemCallback: (item: ItemValue) => boolean;
private waitingGetChoiceDisplayValueResponse: boolean;
Expand All @@ -46,7 +46,7 @@ export class QuestionSelectBase extends Question {
super(name);
this.noneItemValue = this.createDefaultItem(settings.noneItemValue, "noneText", "noneItemText");
this.refuseItemValue = this.createDefaultItem(settings.refuseItemValue, "refuseText", "refuseItemText");
this.donotKnowItemValue = this.createDefaultItem(settings.donotKnowItemValue, "donotKnowText", "donotKnowItemText");
this.dontKnowItemValue = this.createDefaultItem(settings.dontKnowItemValue, "dontKnowText", "dontKnowItemText");
this.createItemValues("choices");
this.registerPropertyChangedHandlers(["choices"], () => {
if (!this.filterItems()) {
Expand All @@ -55,7 +55,7 @@ export class QuestionSelectBase extends Question {
});
this.registerPropertyChangedHandlers(
["choicesFromQuestion", "choicesFromQuestionMode", "choiceValuesFromQuestion",
"choiceTextsFromQuestion", "showNoneItem", "isUsingRestful"],
"choiceTextsFromQuestion", "showNoneItem", "showRefuseItem", "showDontKnowItem", "isUsingRestful"],
() => {
this.onVisibleChoicesChanged();
}
Expand Down Expand Up @@ -244,23 +244,23 @@ export class QuestionSelectBase extends Question {
get locRefuseText(): LocalizableString {
return this.getLocalizableString("refuseText");
}
public get showDonotKnowItem(): boolean {
return this.getPropertyValue("showDonotKnowItem");
public get showDontKnowItem(): boolean {
return this.getPropertyValue("showDontKnowItem");
}
public set showDonotKnowItem(val: boolean) {
this.setPropertyValue("showDonotKnowItem", val);
public set showDontKnowItem(val: boolean) {
this.setPropertyValue("showDontKnowItem", val);
}
public get donotKnowItem(): ItemValue {
return this.donotKnowItemValue;
public get dontKnowItem(): ItemValue {
return this.dontKnowItemValue;
}
public get donotKnowText(): string {
return this.getLocalizableStringText("donotKnowText");
public get dontKnowText(): string {
return this.getLocalizableStringText("dontKnowText");
}
public set donotKnowText(val: string) {
this.setLocalizableStringText("donotKnowText", val);
public set dontKnowText(val: string) {
this.setLocalizableStringText("dontKnowText", val);
}
get locDonotKnowText(): LocalizableString {
return this.getLocalizableString("donotKnowText");
get locDontKnowText(): LocalizableString {
return this.getLocalizableString("dontKnowText");
}
private createDefaultItem(defaultValue: any, name: string, locName: string): ItemValue {
const item = new ItemValue(defaultValue);
Expand Down Expand Up @@ -681,6 +681,8 @@ export class QuestionSelectBase extends Question {
if (!checkEmptyValue && this.isValueEmpty(val)) return false;
if (includeOther && val == this.otherItem.value) return false;
if (this.showNoneItem && val == this.noneItem.value) return false;
if (this.showRefuseItem && val == this.refuseItem.value) return false;
if (this.showDontKnowItem && val == this.dontKnowItem.value) return false;
var choices = isFilteredChoices
? this.getFilteredChoices()
: this.activeChoices;
Expand Down Expand Up @@ -949,6 +951,8 @@ export class QuestionSelectBase extends Question {
return (
!this.isAddDefaultItems &&
!this.showNoneItem &&
!this.showRefuseItem &&
!this.showDontKnowItem &&
!this.hasOther &&
this.choicesOrder == "none"
);
Expand Down Expand Up @@ -996,19 +1000,23 @@ export class QuestionSelectBase extends Question {
}
}
protected addNonChoicesItems(dict: Array<{ index: number, item: ItemValue }>, isAddAll: boolean): void {
if (
this.supportNone() && this.canShowOptionItem(this.noneItem, isAddAll, this.showNoneItem)
) {
this.addNonChoiceItem(dict, this.noneItem, settings.specialChoicesOrder.noneItem);
if (this.supportNone()) {
this.addNonChoiceItem(dict, this.noneItem, isAddAll, this.showNoneItem, settings.specialChoicesOrder.noneItem);
}
if (
this.supportOther() && this.canShowOptionItem(this.otherItem, isAddAll, this.hasOther)
) {
this.addNonChoiceItem(dict, this.otherItem, settings.specialChoicesOrder.otherItem);
if (this.supportRefuse()) {
this.addNonChoiceItem(dict, this.refuseItem, isAddAll, this.showRefuseItem, settings.specialChoicesOrder.refuseItem);
}
if (this.supportDontKnow()) {
this.addNonChoiceItem(dict, this.dontKnowItem, isAddAll, this.showDontKnowItem, settings.specialChoicesOrder.dontKnowItem);
}
if(this.supportOther()) {
this.addNonChoiceItem(dict, this.otherItem, isAddAll, this.hasOther, settings.specialChoicesOrder.otherItem);
}
}
protected addNonChoiceItem(dict: Array<{ index: number, item: ItemValue }>, item: ItemValue, order: Array<number>): void {
order.forEach(val => dict.push({ index: val, item: item }));
protected addNonChoiceItem(dict: Array<{ index: number, item: ItemValue }>, item: ItemValue, isAddAll: boolean, showItem: boolean, order: Array<number>): void {
if(this.canShowOptionItem(item, isAddAll, showItem)) {
order.forEach(val => dict.push({ index: val, item: item }));
}
}
protected canShowOptionItem(item: ItemValue, isAddAll: boolean, hasItem: boolean): boolean {
let res: boolean = (isAddAll && (!!this.canShowOptionItemCallback ? this.canShowOptionItemCallback(item) : true)) || hasItem;
Expand All @@ -1021,6 +1029,8 @@ export class QuestionSelectBase extends Question {
public isItemInList(item: ItemValue): boolean {
if (item === this.otherItem) return this.hasOther;
if (item === this.noneItem) return this.showNoneItem;
if (item === this.refuseItem) return this.showRefuseItem;
if (item === this.dontKnowItem) return this.showDontKnowItem;
if (item === this.newItemValue) return false;
return true;
}
Expand Down Expand Up @@ -1192,7 +1202,7 @@ export class QuestionSelectBase extends Question {
: undefined;
const choices = question.visibleChoices;
for (var i = 0; i < choices.length; i++) {
if (this.isBuiltInChoice(choices[i], question)) continue;
if (question.isBuiltInChoice(choices[i])) continue;
if (isSelected === undefined) {
res.push(this.copyChoiceItem(choices[i]));
continue;
Expand All @@ -1219,16 +1229,20 @@ export class QuestionSelectBase extends Question {
choices = this.visibleChoices;
}
for (var i = 0; i < choices.length; i++) {
if (!this.isBuiltInChoice(choices[i], this)) return true;
if (!this.isBuiltInChoice(choices[i])) return true;
}
return false;
}
protected isBuiltInChoice(item: ItemValue, question: QuestionSelectBase): boolean {
return (
item === question.noneItem ||
item === question.otherItem ||
item === question.newItemValue
);
protected isBuiltInChoice(item: ItemValue): boolean {
return this.isNoneItem(item) ||
item === this.otherItem ||
item === this.newItemValue;
}
public isNoneItem(item: ItemValue): boolean {
return this.getNoneItems().indexOf(item) > -1;
}
protected getNoneItems(): Array<ItemValue> {
return [this.noneItem, this.refuseItem, this.dontKnowItem];
}
protected getChoices(): Array<ItemValue> {
return this.choices;
Expand All @@ -1239,6 +1253,12 @@ export class QuestionSelectBase extends Question {
public supportNone(): boolean {
return this.isSupportProperty("showNoneItem");
}
public supportRefuse(): boolean {
return this.isSupportProperty("showRefuseItem");
}
public supportDontKnow(): boolean {
return this.isSupportProperty("showDontKnowItem");
}
protected isSupportProperty(propName: string): boolean {
return (
!this.isDesignMode ||
Expand Down Expand Up @@ -1679,7 +1699,7 @@ export class QuestionSelectBase extends Question {
return res;
}
get dataChoices(): ItemValue[] {
return this.visibleChoices.filter((item) => !this.isBuiltInChoice(item, this));
return this.visibleChoices.filter((item) => !this.isBuiltInChoice(item));
}
get bodyItems(): ItemValue[] {
return (this.hasHeadItems || this.hasFootItems) ? this.dataChoices : this.visibleChoices;
Expand Down Expand Up @@ -1965,7 +1985,7 @@ Serializer.addClass(
{ name: "showOtherItem:boolean", alternativeName: "hasOther" },
{ name: "showNoneItem:boolean", alternativeName: "hasNone" },
{ name: "showRefuseItem:boolean", visible: false },
{ name: "showDonotKnowItem:boolean", visible: false },
{ name: "showDontKnowItem:boolean", visible: false },
{
name: "otherPlaceholder",
alternativeName: "otherPlaceHolder",
Expand All @@ -1992,11 +2012,11 @@ Serializer.addClass(
},
},
{
name: "donotKnowText",
serializationProperty: "locDonotKnowText",
dependsOn: "showDonotKnowItem",
name: "dontKnowText",
serializationProperty: "locDontKnowText",
dependsOn: "showDontKnowItem",
visibleIf: function (obj: any) {
return obj.showDonotKnowItem;
return obj.showDontKnowItem;
},
},
{
Expand Down
104 changes: 67 additions & 37 deletions src/question_checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
* @see showSelectAllItem
*/
public get isAllSelected(): boolean {
if (this.isItemSelected(this.noneItem)) return false;
const noneItems = this.getNoneItems();
for(let i = 0; i < noneItems.length; i ++) {
if(this.isItemSelected(noneItems[i])) return false;
}
const items = this.getVisibleEnableItems();
if(items.length === 0) return false;
const val = this.value;
Expand Down Expand Up @@ -162,20 +165,34 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
this.toggleSelectAll();
}
} else {
const newValue: Array<any> = [].concat(this.renderedValue || []);
const index = newValue.indexOf(item.value);
if (checked) {
if (index < 0) {
newValue.push(item.value);
}
if(this.isNoneItem(item)) {
this.renderedValue = [item.value];
} else {
if (index > -1) {
newValue.splice(index, 1);
const newValue: Array<any> = [].concat(this.renderedValue || []);
const index = newValue.indexOf(item.value);
if (checked) {
if (index < 0) {
newValue.push(item.value);
}
} else {
if (index > -1) {
newValue.splice(index, 1);
}
}
this.renderedValue = newValue;
}
this.renderedValue = newValue;
}
}
private removeNoneItems(val: Array<any>): Array<any> {
const items = this.getNoneItems();
items.forEach(item => {
const index = val.indexOf(item.value);
if(index > -1) {
val.splice(index, 1);
}
});
return val;
}
protected isItemSelectedCore(item: ItemValue): boolean {
if (item === this.selectAllItem) return this.isAllSelected;
var val = this.renderedValue;
Expand Down Expand Up @@ -374,20 +391,7 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
if (!newValue) newValue = [];
if (!value) value = [];
if (this.isTwoValueEquals(value, newValue)) return;
if (this.showNoneItem) {
var prevNoneIndex = this.noneIndexInArray(value);
var newNoneIndex = this.noneIndexInArray(newValue);
if (prevNoneIndex > -1) {
if (newNoneIndex > -1 && newValue.length > 1) {
newValue.splice(newNoneIndex, 1);
}
} else {
if (newNoneIndex > -1) {
newValue.splice(0, newValue.length);
newValue.push(this.noneItem.value);
}
}
}
this.removeNoneItemsValues(value, newValue);
super.setNewValue(newValue);
}
protected getIsMultipleValue(): boolean {
Expand All @@ -411,30 +415,56 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
}
return -1;
}
private noneIndexInArray(val: any) {
if (!val || !Array.isArray(val)) return -1;
var noneValue = this.noneItem.value;
for (var i = 0; i < val.length; i++) {
if (val[i] == noneValue) return i;
protected removeNoneItemsValues(value: Array<any>, newValue: Array<any>): void {
const noneValues: Array<any> = [];
if(this.showNoneItem) noneValues.push(this.noneItem.value);
if(this.showRefuseItem) noneValues.push(this.refuseItem.value);
if(this.showDontKnowItem) noneValues.push(this.dontKnowItem.value);
if (noneValues.length > 0) {
const prevNone = this.noneIndexInArray(value, noneValues);
const newNone = this.noneIndexInArray(newValue, noneValues);
if(prevNone.index > -1) {
if(prevNone.val === newNone.val) {
if(newValue.length > 0) {
newValue.splice(newNone.index, 1);
}
} else {
const prevNewNone = this.noneIndexInArray(newValue, [prevNone.val]);
if(prevNewNone.index > -1 && prevNewNone.index < newValue.length - 1) {
newValue.splice(prevNewNone.index, 1);
}
}
} else {
if (newNone.index > -1 && newValue.length > 1) {
const itemVal = this.convertValueToObject([newNone.val])[0];
newValue.splice(0, newValue.length, itemVal);
}
}
}
return -1;
}

private noneIndexInArray(val: any, noneValues: Array<any>): { index: number, val: any } {
if (!Array.isArray(val)) return { index: -1, val: undefined };
for (var i = val.length - 1; i >= 0; i--) {
const index = noneValues.indexOf(this.getRealValue(val[i]));
if (index > -1) return { index: i, val: noneValues[index] };
}
return { index: -1, val: undefined };
}
protected canUseFilteredChoices(): boolean {
return !this.hasSelectAll && super.canUseFilteredChoices();
}
protected supportSelectAll() {
protected supportSelectAll(): boolean {
return this.isSupportProperty("showSelectAllItem");
}
protected addNonChoicesItems(dict: Array<{ index: number, item: ItemValue }>, isAddAll: boolean): void {
super.addNonChoicesItems(dict, isAddAll);
if (
this.supportSelectAll() && this.canShowOptionItem(this.selectAllItem, isAddAll, this.hasSelectAll)
) {
this.addNonChoiceItem(dict, this.selectAllItem, settings.specialChoicesOrder.selectAllItem);
if (this.supportSelectAll()) {
this.addNonChoiceItem(dict, this.selectAllItem, isAddAll, this.hasSelectAll, settings.specialChoicesOrder.selectAllItem);
}
}
protected isBuiltInChoice(item: ItemValue, question: QuestionSelectBase): boolean {
return item === (<QuestionCheckboxBase>question).selectAllItem || super.isBuiltInChoice(item, question);
protected isBuiltInChoice(item: ItemValue): boolean {
return item === this.selectAllItem || super.isBuiltInChoice(item);
}

public isItemInList(item: ItemValue): boolean {
Expand Down
14 changes: 7 additions & 7 deletions src/question_imagepicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,10 @@ export class QuestionImagePickerModel extends QuestionCheckboxBase {
public get isCompositeQuestion(): boolean {
return true;
}
public supportOther(): boolean {
return false;
}
public supportNone(): boolean {
return false;
}
public supportOther(): boolean { return false; }
public supportNone(): boolean { return false; }
public supportRefuse(): boolean { return false; }
public supportDontKnow(): boolean { return false; }
public isAnswerCorrect(): boolean {
if (!this.multiSelect) return super.isAnswerCorrect();
return Helpers.isArrayContainsEqual(this.value, this.correctAnswer);
Expand Down Expand Up @@ -291,7 +289,7 @@ export class QuestionImagePickerModel extends QuestionCheckboxBase {
return this.multiSelect ? "checkbox" : "radio";
}

protected isBuiltInChoice(item: ItemValue, question: QuestionSelectBase): boolean {
protected isBuiltInChoice(item: ItemValue): boolean {
return false;
}
protected addToVisibleChoices(items: Array<ItemValue>, isAddAll: boolean): void {
Expand Down Expand Up @@ -442,6 +440,8 @@ Serializer.addClass(
{ name: "showOtherItem", visible: false },
{ name: "otherText", visible: false },
{ name: "showNoneItem", visible: false },
{ name: "showRefuseItem", visible: false },
{ name: "showDontKnowItem", visible: false },
{ name: "noneText", visible: false },
{ name: "optionsCaption", visible: false },
{ name: "otherErrorText", visible: false },
Expand Down
Loading

0 comments on commit c7ae838

Please sign in to comment.