diff --git a/CHANGELOG.md b/CHANGELOG.md
index b7cf179aae..26ebfb6262 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
+## [1.9.91](https://github.com/surveyjs/survey-library/compare/v1.9.90...v1.9.91) (2023-06-08)
+
## [1.9.90](https://github.com/surveyjs/survey-library/compare/v1.9.89...v1.9.90) (2023-05-31)
## [1.9.89](https://github.com/surveyjs/survey-library/compare/v1.9.88...v1.9.89) (2023-05-23)
diff --git a/package.json b/package.json
index 74f2134595..703c15e166 100644
--- a/package.json
+++ b/package.json
@@ -81,7 +81,7 @@
"testcafe:ci:angular": "testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless testCafe/ --app \"http-server ./packages/survey-angular-ui/example/dist --proxy http://localhost:8080? -p 8080\" --selector-timeout 1500 --reporter minimal --env=angular",
"prepare": "husky install"
},
- "version": "1.9.90",
+ "version": "1.9.91",
"name": "survey-library",
"private": true,
"devDependencies": {
diff --git a/packages/survey-angular-ui/CHANGELOG.md b/packages/survey-angular-ui/CHANGELOG.md
index 327b95ea74..065065aa2e 100644
--- a/packages/survey-angular-ui/CHANGELOG.md
+++ b/packages/survey-angular-ui/CHANGELOG.md
@@ -2,6 +2,8 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
+## [1.9.91](https://github.com/surveyjs/surveyjs/compare/v1.9.90...v1.9.91) (2023-06-08)
+
## [1.9.90](https://github.com/surveyjs/surveyjs/compare/v1.9.89...v1.9.90) (2023-05-31)
## [1.9.89](https://github.com/surveyjs/surveyjs/compare/v1.9.88...v1.9.89) (2023-05-23)
diff --git a/packages/survey-angular-ui/package.json b/packages/survey-angular-ui/package.json
index 750f94c4a0..8cccf8c67d 100644
--- a/packages/survey-angular-ui/package.json
+++ b/packages/survey-angular-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "survey-angular-ui",
- "version": "1.9.90",
+ "version": "1.9.91",
"description": "survey.js is a JavaScript Survey Library. It is a modern way to add a survey to your website. It uses JSON for survey metadata and results.",
"keywords": [
"Survey",
diff --git a/packages/survey-angular-ui/src/components/dropdown/dropdown.component.html b/packages/survey-angular-ui/src/components/dropdown/dropdown.component.html
index c47581b32d..ea91090b60 100644
--- a/packages/survey-angular-ui/src/components/dropdown/dropdown.component.html
+++ b/packages/survey-angular-ui/src/components/dropdown/dropdown.component.html
@@ -18,7 +18,7 @@
-
+
;
protected popupCssClasses = "sv-single-select-list";
protected listModelFilterStringChanged = (newValue: string) => {
- if(this.filterString !== newValue) {
+ if (this.filterString !== newValue) {
this.filterString = newValue;
}
}
@@ -93,7 +93,7 @@ export class DropdownListModel extends Base {
});
this._popupModel.cssClass = this.popupCssClasses;
this._popupModel.onVisibilityChanged.add((_, option: { isVisible: boolean }) => {
- if(option.isVisible) {
+ if (option.isVisible) {
this.listModel.renderElements = true;
}
if (option.isVisible && this.question.choicesLazyLoadEnabled) {
@@ -220,11 +220,9 @@ export class DropdownListModel extends Base {
defaultValue: "",
onSet: (newValue, target: DropdownListModel) => {
target.question.inputHasValue = !!newValue;
- target.showSelectedItemLocText = target.question.showSelectedItemLocText;
}
}) inputString: string;
- @property({}) showSelectedItemLocText: boolean;
@property({}) showInputFieldComponent: boolean;
@property() ariaActivedescendant: string;
@@ -317,11 +315,9 @@ export class DropdownListModel extends Base {
question.onPropertyChanged.add((sender: any, options: any) => {
if (options.name == "value") {
this.showInputFieldComponent = this.question.showInputFieldComponent;
- this.showSelectedItemLocText = this.question.showSelectedItemLocText;
}
});
this.showInputFieldComponent = this.question.showInputFieldComponent;
- this.showSelectedItemLocText = this.question.showSelectedItemLocText;
this.listModel = this.createListModel();
this.updateAfterListModelCreated(this.listModel);
@@ -508,10 +504,10 @@ export class DropdownListModel extends Base {
dispose(): void {
super.dispose();
- if(!!this.listModel) {
+ if (!!this.listModel) {
this.listModel.dispose();
}
- if(!!this.popupModel) {
+ if (!!this.popupModel) {
this.popupModel.dispose();
}
}
diff --git a/src/question_baseselect.ts b/src/question_baseselect.ts
index cecc09a6f2..1512f042c5 100644
--- a/src/question_baseselect.ts
+++ b/src/question_baseselect.ts
@@ -323,7 +323,9 @@ export class QuestionSelectBase extends Question {
protected onEnableItemCallBack(item: ItemValue): boolean {
return true;
}
- protected onSelectedItemValuesChangedHandler(newValue: any): void { }
+ protected onSelectedItemValuesChangedHandler(newValue: any): void {
+ this.survey?.loadedChoicesFromServer(this);
+ }
protected getItemIfChoicesNotContainThisValue(value: any, text?: string): any {
if(!this.isReady) {
return this.createItemValue(value, text);
@@ -343,6 +345,9 @@ export class QuestionSelectBase extends Question {
return itemValue || selectedItemValues || (this.isOtherSelected ? this.otherItem : this.getItemIfChoicesNotContainThisValue(this.value));
}
protected onGetSingleSelectedItem(selectedItemByValue: ItemValue): void {}
+ protected getMultipleSelectedItems(): Array {
+ return [];
+ }
private setConditionalChoicesRunner() {
if (this.choicesVisibleIf) {
if (!this.conditionChoicesVisibleIfRunner) {
@@ -951,6 +956,8 @@ export class QuestionSelectBase extends Question {
protected getChoicesDisplayValue(items: ItemValue[], val: any): any {
if (val == this.otherItemValue.value)
return this.otherValue ? this.otherValue : this.locOtherText.textOrHtml;
+ const selItem = this.getSingleSelectedItem();
+ if(!!selItem && selItem.value === val) return selItem.locText.textOrHtml;
var str = ItemValue.getTextOrHtmlByValue(items, val);
return str == "" && val ? val : str;
}
@@ -958,11 +965,19 @@ export class QuestionSelectBase extends Question {
onGetValueCallback?: (index: number) => any): string {
var items = this.visibleChoices;
var strs = [];
+ const vals = [];
for (var i = 0; i < value.length; i++) {
- let val = !onGetValueCallback ? value[i] : onGetValueCallback(i);
- let valStr = this.getChoicesDisplayValue(items, val);
- if (valStr) {
- strs.push(valStr);
+ vals.push(!onGetValueCallback ? value[i] : onGetValueCallback(i));
+ }
+ if(Helpers.isTwoValueEquals(this.value, vals)) {
+ this.getMultipleSelectedItems().forEach(item => strs.push(item.locText.textOrHtml));
+ }
+ if(strs.length === 0) {
+ for (var i = 0; i < vals.length; i++) {
+ let valStr = this.getChoicesDisplayValue(items, vals[i]);
+ if (valStr) {
+ strs.push(valStr);
+ }
}
}
return strs.join(", ");
@@ -1180,12 +1195,11 @@ export class QuestionSelectBase extends Question {
if (this.enableOnLoadingChoices) {
this.readOnly = false;
}
+ const errors = [];
if (!this.isReadOnly) {
- var errors = [];
if (this.choicesByUrl && this.choicesByUrl.error) {
errors.push(this.choicesByUrl.error);
}
- this.errors = errors;
}
var newChoices = null;
var checkCachedValuesOnExisting = true;
@@ -1240,6 +1254,10 @@ export class QuestionSelectBase extends Question {
}
}
}
+ if(!this.isReadOnly && !newChoices && !this.isFirstLoadChoicesFromUrl) {
+ this.value = null;
+ }
+ this.errors = errors;
this.choicesLoaded();
}
private createCachedValueForUrlRequests(
diff --git a/src/question_checkbox.ts b/src/question_checkbox.ts
index 445bc05401..5078831366 100644
--- a/src/question_checkbox.ts
+++ b/src/question_checkbox.ts
@@ -200,6 +200,9 @@ export class QuestionCheckboxModel extends QuestionCheckboxBase {
return this.validateItemValues(itemValues);
}
public get selectedItems(): Array { return this.selectedChoices; }
+ protected getMultipleSelectedItems(): Array {
+ return this.selectedChoices;
+ }
protected validateItemValues(itemValues: Array): Array {
if(!!itemValues.length) return itemValues;
diff --git a/src/question_dropdown.ts b/src/question_dropdown.ts
index 29cfbe568e..d52dc6b637 100644
--- a/src/question_dropdown.ts
+++ b/src/question_dropdown.ts
@@ -266,6 +266,7 @@ export class QuestionDropdownModel extends QuestionSelectBase {
}
protected onSelectedItemValuesChangedHandler(newValue: any): void {
this.dropdownListModel?.setInputStringFromSelectedItem(newValue);
+ super.onSelectedItemValuesChangedHandler(newValue);
}
protected hasUnknownValue(
val: any,
diff --git a/src/question_matrixdynamic.ts b/src/question_matrixdynamic.ts
index 4f08eb7798..d5e2c5ac80 100644
--- a/src/question_matrixdynamic.ts
+++ b/src/question_matrixdynamic.ts
@@ -89,7 +89,13 @@ export class QuestionMatrixDynamicModel extends QuestionMatrixDropdownModelBase
private draggedRow: MatrixDropdownRowModelBase;
private isBanStartDrag(pointerDownEvent: PointerEvent): boolean {
const target = (pointerDownEvent.target);
- return target.getAttribute("contenteditable") === "true" || target.nodeName === "INPUT";
+ return target.getAttribute("contenteditable") === "true" || target.nodeName === "INPUT" || !this.isDragHandleAreaValid(target);
+ }
+ public isDragHandleAreaValid(node:HTMLElement): boolean {
+ if (this.survey.matrixDragHandleArea === "icon") {
+ return node.classList.contains(this.cssClasses.dragElementDecorator);
+ }
+ return true;
}
public onPointerDown(pointerDownEvent: PointerEvent, row: MatrixDropdownRowModelBase):void {
if (!row || !this.allowRowsDragAndDrop) return;
diff --git a/src/survey.ts b/src/survey.ts
index bc43c55dcd..90d27c6c3e 100644
--- a/src/survey.ts
+++ b/src/survey.ts
@@ -2925,6 +2925,20 @@ export class SurveyModel extends SurveyElementCore
public get isShowStartingPage(): boolean {
return this.state === "starting";
}
+ /**
+ * Specifies which part of a choice item responds to a drag gesture in MatrixDynamic questions.
+ *
+ * Possible values:
+ *
+ * - `"entireItem"` (default) - Users can use the entire choice item as a drag handle.
+ * - `"icon"` - Users can only use the choice item icon as a drag handle.
+ */
+ public get matrixDragHandleArea():string {
+ return this.getPropertyValue("matrixDragHandleArea", "entireItem");
+ }
+ public set matrixDragHandleArea(val: string) {
+ this.setPropertyValue("matrixDragHandleArea", val);
+ }
/**
* Survey is showing a page right now. It is in "running", "preview" or starting state.
*/
@@ -7082,6 +7096,12 @@ Serializer.addClass("survey", [
default: "initial",
choices: ["initial", "random"],
},
+ {
+ name: "matrixDragHandleArea",
+ visible: false,
+ default: "entireItem",
+ choices: ["entireItem", "icon"]
+ },
"showPageNumbers:boolean",
{
name: "showQuestionNumbers",
diff --git a/tests/choicesRestfultests.ts b/tests/choicesRestfultests.ts
index b2049e64ce..f3d2de47a0 100644
--- a/tests/choicesRestfultests.ts
+++ b/tests/choicesRestfultests.ts
@@ -13,6 +13,7 @@ import { JsonObject, Serializer } from "../src/jsonobject";
import { QuestionRadiogroupModel } from "../src/question_radiogroup";
import { settings } from "../src/settings";
import { MatrixDropdownColumn } from "../src/question_matrixdropdowncolumn";
+import { SurveyError } from "../src/survey-error";
export default QUnit.module("choicesRestful");
@@ -719,8 +720,24 @@ QUnit.test("Set value before loading data, bug #1089", function(assert) {
question.hasItemsCallbackDelay = true;
question.onSurveyLoad();
survey.setValue("q1", "CA");
- question.doResultsCallback();
+ question["onLoadChoicesFromUrl"]([new ItemValue("CA")]);
assert.equal(question.value, "CA", "'CA' value is still here");
+ assert.equal(question.selectedItem.value, "CA", "selectedItem is correct");
+});
+QUnit.test("Clear value on getting empty array, bug #6251", function(assert) {
+ var survey = new SurveyModel();
+ survey.addNewPage("1");
+ var question = new QuestionDropdownModelTester("q1");
+ question.choicesByUrl.url = "{state}";
+ survey.pages[0].addQuestion(question);
+ question.hasItemsCallbackDelay = true;
+ question.onSurveyLoad();
+ survey.setValue("q1", "CA");
+ question.choicesByUrl.error = new SurveyError("Empty request");
+ question["onLoadChoicesFromUrl"]([]);
+ assert.equal(question.isEmpty(), true, "value is empty");
+ assert.equal(question.selectedItem, null, "selectedItem is null");
+ assert.equal(question.errors.length, 1, "It shows error on empty result");
});
QUnit.test(
@@ -736,9 +753,10 @@ QUnit.test(
question.onSurveyLoad();
survey.setValue("q1", "CA");
assert.equal(question.isOtherSelected, false, "There shuld not be other#1");
- question.doResultsCallback();
+ question["onLoadChoicesFromUrl"]([new ItemValue("CA")]);
assert.equal(question.isOtherSelected, false, "There shuld not be other#2");
assert.equal(question.value, "CA", "'CA' value is still here");
+ assert.equal(question.selectedItem.value, "CA", "selectedItem is correct");
}
);
@@ -748,20 +766,21 @@ QUnit.test("preset data and same data from url", function(assert) {
survey.addNewPage("1");
var question = new QuestionDropdownModelTester("q1");
survey.storeOthersAsComment = false;
-
+ question.hasItemsCallbackDelay = true;
+ question.choicesByUrl.url = "{state}";
+ survey.pages[0].addQuestion(question);
+ question.onSurveyLoad();
+ survey.data = { q1: "CA" };
survey.onValueChanging.add(function() {
counter++;
});
-
survey.onValueChanged.add(function() {
counter++;
});
-
- question.choicesByUrl.url = "{state}";
- survey.pages[0].addQuestion(question);
- survey.data = { q1: "CA" };
+ assert.equal(question.value, "CA", "value is here");
question["onLoadChoicesFromUrl"]([new ItemValue("CA"), new ItemValue("AA")]);
-
+ assert.equal(question.value, "CA", "value is still here");
+ assert.equal(question.selectedItem.value, "CA", "selecteditem is correct");
assert.equal(counter, 0, "value doesn't change");
});
diff --git a/tests/questionDropdownTests.ts b/tests/questionDropdownTests.ts
index 67429fc331..c1d394078e 100644
--- a/tests/questionDropdownTests.ts
+++ b/tests/questionDropdownTests.ts
@@ -2,6 +2,7 @@ import { SurveyModel } from "../src/survey";
import { QuestionDropdownModel } from "../src/question_dropdown";
import { ListModel } from "../src/list";
import { createListContainerHtmlElement } from "./utilstests";
+import { Question } from "../src/question";
import { settings } from "../src/settings";
export default QUnit.module("choicesRestful");
@@ -1049,7 +1050,13 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: defaultValue", assert => {
"name": "q1",
"defaultValue": 55,
"choicesLazyLoadEnabled": true
- }]
+ },
+ {
+ "type": "text",
+ "name": "q2",
+ "title": "{q1}"
+ }
+ ]
};
const survey = new SurveyModel(json);
survey.onChoicesLazyLoad.add((sender, options) => {
@@ -1069,11 +1076,13 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: defaultValue", assert => {
});
const question = survey.getAllQuestions()[0];
+ const questionTitle = survey.getAllQuestions()[1];
assert.equal(question.choicesLazyLoadEnabled, true);
assert.equal(question.choices.length, 0);
assert.equal(question.value, 55);
assert.equal(question.selectedItem.value, 55);
assert.equal(question.selectedItem.text, "DisplayText_55");
+ assert.equal(questionTitle.locTitle.textOrHtml, "DisplayText_55", "display text is correct");
question.dropdownListModel.popupModel.isVisible = true;
setTimeout(() => {
diff --git a/tests/question_matrixdynamictests.ts b/tests/question_matrixdynamictests.ts
index 709c48533c..20f970f0f6 100644
--- a/tests/question_matrixdynamictests.ts
+++ b/tests/question_matrixdynamictests.ts
@@ -8185,4 +8185,37 @@ QUnit.test("Check rightIndents set correctly for detailElements with defaultV2 t
const matrix = survey.getQuestionByName("matrix");
matrix.visibleRows[0].showHideDetailPanelClick();
assert.equal(matrix.renderedTable.rows[1].cells[1].panel.elements[0].rightIndent, 0);
+});
+
+QUnit.test("matrixDragHandleArea = 'icon'", function (assert) {
+ const survey = new SurveyModel({
+ matrixDragHandleArea: "icon",
+ elements: [
+ {
+ type: "matrixdynamic",
+ allowRowsDragAndDrop: "true",
+ name: "matrix",
+ rowCount: 2,
+ detailPanelMode: "underRow",
+ detailPanelShowOnAdding: true,
+ columns: [{ name: "col1" }, { name: "col2" }, { name: "col3" }],
+ detailElements: [{ type: "text", name: "q1" }, { type: "text", name: "q2", startWithNewLine: false, visibleIf: "{row.q1} notempty" }],
+ },
+ ],
+ });
+ survey.css = { root: "sd-root-modern" };
+ const matrix = survey.getQuestionByName("matrix");
+ let nodeMock = document.createElement("div");
+ assert.equal(matrix.isDragHandleAreaValid(nodeMock), false);
+
+ nodeMock.classList.add("some-class");
+ assert.equal(matrix.isDragHandleAreaValid(nodeMock), false);
+
+ nodeMock.classList.add(matrix.cssClasses.dragElementDecorator);
+ assert.equal(matrix.isDragHandleAreaValid(nodeMock), true);
+
+ survey.matrixDragHandleArea = "entireItem";
+
+ nodeMock.classList.remove(matrix.cssClasses.dragElementDecorator);
+ assert.equal(matrix.isDragHandleAreaValid(nodeMock), true);
});
\ No newline at end of file
diff --git a/tests/question_tagbox_tests.ts b/tests/question_tagbox_tests.ts
index 61b9880fc2..0c3feeb77a 100644
--- a/tests/question_tagbox_tests.ts
+++ b/tests/question_tagbox_tests.ts
@@ -547,8 +547,12 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: defaultValue", assert => {
"name": "q1",
"defaultValue": [52, 55],
"choicesLazyLoadEnabled": true
- }]
- };
+ },
+ {
+ "type": "text",
+ "name": "q2",
+ "title": "{q1}"
+ }] };
const survey = new SurveyModel(json);
survey.onChoicesLazyLoad.add((sender, options) => {
const total = 55;
@@ -567,6 +571,7 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: defaultValue", assert => {
});
const question = survey.getAllQuestions()[0];
+ const questionTitle = survey.getAllQuestions()[1];
assert.equal(question.choicesLazyLoadEnabled, true);
assert.equal(question.choices.length, 0);
assert.deepEqual(question.value, [52, 55]);
@@ -575,6 +580,7 @@ QUnit.test("lazy loading + onGetChoiceDisplayValue: defaultValue", assert => {
assert.equal(question.selectedItems[0].text, "DisplayText_52", "question.selectedItems[0] text");
assert.equal(question.selectedItems[1].value, 55, "question.selectedItems[1] value");
assert.equal(question.selectedItems[1].text, "DisplayText_55", "question.selectedItems[1] text");
+ assert.equal(questionTitle.locTitle.textOrHtml, "DisplayText_52, DisplayText_55", "display text is correct");
question.dropdownListModel.popupModel.isVisible = true;
setTimeout(() => {