Skip to content

Commit

Permalink
Allow to set empty string to localizable string with default value #6758
Browse files Browse the repository at this point in the history
 (#6760)

* Allow to set empty string to localizable string with default value #6758

* Add localizable string, clearLocale function #6759

* Add unit test on empty string for string property #6759

* Allow to change editingObject property to empty string

* Support empty string in serilaizer #6759
  • Loading branch information
andrewtelnov authored Sep 4, 2023
1 parent d8c92c3 commit cd15077
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 30 deletions.
8 changes: 7 additions & 1 deletion src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,13 @@ export class Base {
return this.getDefaultPropertyValue(name) !== undefined;
}
public resetPropertyValue(name: string): void {
this.setPropertyValue(name, undefined);
const locStr = this.localizableStrings ? this.localizableStrings[name] : undefined;
if(locStr) {
locStr.clearLocale();
}
else {
this.setPropertyValue(name, undefined);
}
}
protected getPropertyValueWithoutDefault(name: string): any {
return this.getPropertyValueCore(this.propertyHash, name);
Expand Down
8 changes: 4 additions & 4 deletions src/jsonobject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,10 @@ export class JsonObjectProperty implements IObject {
if (!Helpers.isValueEmpty(this.defaultValue)) {
return Helpers.isTwoValueEquals(value, this.defaultValue, false, true, false);
}
if(this.isLocalizable) return value === null || value === undefined;
return (
(value === false && (this.type == "boolean" || this.type == "switch")) ||
value === "" ||
Helpers.isValueEmpty(value)
value === "" || Helpers.isValueEmpty(value)
);
}
public getValue(obj: any): any {
Expand All @@ -349,7 +349,7 @@ export class JsonObjectProperty implements IObject {
if (!this.onSettingValue || obj.isLoadingFromJson) return value;
return this.onSettingValue(obj, value);
}
public setValue(obj: any, value: any, jsonConv: JsonObject) {
public setValue(obj: any, value: any, jsonConv: JsonObject): void {
if (this.onSetValue) {
this.onSetValue(obj, value, jsonConv);
} else {
Expand Down Expand Up @@ -1603,7 +1603,7 @@ export class JsonObject {
}
}
public valueToObj(value: any, obj: any, property: JsonObjectProperty) {
if (value == null) return;
if (value === null || value === undefined) return;
this.removePos(property, value);
if (property != null && property.hasToUseSetValue) {
property.setValue(obj, value, this);
Expand Down
55 changes: 34 additions & 21 deletions src/localizablestring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,19 @@ export class LocalizableString implements ILocalizableString {
var loc = this.locale;
if (!loc) loc = this.defaultLoc;
var res = this.getValue(loc);
if (!res && loc === this.defaultLoc) {
if (this.isValueEmpty(res) && loc === this.defaultLoc) {
res = this.getValue(surveyLocalization.defaultLocale);
}
if(!res) {
if(this.isValueEmpty(res)) {
const dialect = this.getRootDialect(loc);
if(!!dialect) {
res = this.getValue(dialect);
}
}
if (!res && loc !== this.defaultLoc) {
if (this.isValueEmpty(res) && loc !== this.defaultLoc) {
res = this.getValue(this.defaultLoc);
}
if (!res && !!this.getLocalizationName()) {
if (this.isValueEmpty(res) && !!this.getLocalizationName()) {
res = this.getLocalizationStr();
if(!!this.onGetLocalizationTextCallback) {
res = this.onGetLocalizationTextCallback(res);
Expand All @@ -141,47 +141,55 @@ export class LocalizableString implements ILocalizableString {
public get hasHtml(): boolean {
return this.hasHtmlValue();
}
public get html() {
public get html(): string {
if (!this.hasHtml) return "";
return this.getHtmlValue();
}
public get isEmpty(): boolean {
return this.getValuesKeys().length == 0;
}
public get textOrHtml() {
public get textOrHtml(): string {
return this.hasHtml ? this.getHtmlValue() : this.calculatedText;
}
public get renderedHtml() {
public get renderedHtml(): string {
return this.textOrHtml;
}
public getLocaleText(loc: string): string {
if (!loc) loc = this.defaultLoc;
var res = this.getValue(loc);
const res = this.getLocaleTextCore(loc);
return res ? res : "";
}
private getLocaleTextWithDefault(loc: string): string {
let res = this.getLocaleText(loc);
private getLocaleTextCore(loc: string): string {
if (!loc) loc = this.defaultLoc;
return this.getValue(loc);
}
private isLocaleTextEqualsWithDefault(loc: string, val: string): boolean {
let res = this.getLocaleTextCore(loc);
if(!res && this.onGetDefaultTextCallback) {
return this.onGetDefaultTextCallback();
res = this.onGetDefaultTextCallback();
}
return res;
if(res === val) return true;
return this.isValueEmpty(res) && this.isValueEmpty(val);
}
public clear(): void {
this.setJson(undefined);
}
public clearLocale(loc?: string): void {
this.setLocaleText(loc, undefined);
}
public setLocaleText(loc: string, value: string): void {
loc = this.getValueLoc(loc);
if (!this.storeDefaultText && value == this.getLocaleTextWithDefault(loc)) {
if(!!value || !!loc && loc !== this.defaultLoc) return;
if (!this.storeDefaultText && this.isLocaleTextEqualsWithDefault(loc, value)) {
if(!this.isValueEmpty(value) || !!loc && loc !== this.defaultLoc) return;
let dl = surveyLocalization.defaultLocale;
let oldValue = this.getValue(dl);
if(!!dl && !!oldValue) {
if(!!dl && !this.isValueEmpty(oldValue)) {
this.setValue(dl, value);
this.fireStrChanged(dl, oldValue);
}
return;
}
if (!settings.localization.storeDuplicatedTranslations &&
value &&
loc &&
loc != this.defaultLoc &&
!this.isValueEmpty(value) && loc && loc != this.defaultLoc &&
!this.getValue(loc) &&
value == this.getLocaleText(this.defaultLoc)
)
Expand All @@ -190,7 +198,7 @@ export class LocalizableString implements ILocalizableString {
if (!loc) loc = this.defaultLoc;
var oldValue = this.onStrChanged && loc === curLoc ? this.pureText : undefined;
delete (<any>this).htmlValues[loc];
if (!value) {
if (this.isValueEmpty(value)) {
if (this.getValue(loc)) this.deleteValue(loc);
} else {
if (typeof value === "string") {
Expand All @@ -206,6 +214,11 @@ export class LocalizableString implements ILocalizableString {
}
this.fireStrChanged(loc, oldValue);
}
private isValueEmpty(val: string): boolean {
if(val === undefined || val === null) return true;
if(this.localizationName) return false;
return val === "";
}
private get curLocale(): string {
return !!this.locale ? this.locale : this.defaultLoc;
}
Expand Down Expand Up @@ -258,7 +271,7 @@ export class LocalizableString implements ILocalizableString {
}
this.values = {};
this.htmlValues = {};
if (!value) return;
if (value === null || value === undefined) return;
if (typeof value === "string") {
this.setLocaleText(null, value);
} else {
Expand Down
7 changes: 6 additions & 1 deletion src/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6123,7 +6123,7 @@ export class SurveyModel extends SurveyElementCore
)
return;
var oldValue = this.getValue(name);
if (this.isValueEmpty(newValue, false)) {
if (this.isValueEmpyOnSetValue(name, newValue)) {
this.deleteDataValueCore(this.valuesHash, name);
} else {
newValue = this.getUnbindValue(newValue);
Expand All @@ -6137,6 +6137,11 @@ export class SurveyModel extends SurveyElementCore
allowNotifyValueChanged
);
}
private isValueEmpyOnSetValue(name: string, val: any): boolean {
if(!this.isValueEmpty(val, false)) return false;
if(!this.editingObj || val === null || val === undefined) return true;
return this.editingObj.getDefaultPropertyValue(name) === val;
}
private updateOnSetValue(
name: string,
newValue: any,
Expand Down
11 changes: 11 additions & 0 deletions tests/basetests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,4 +714,15 @@ QUnit.test("base.hasDefaultPropertyValue, base.getDefaultPropertyValue and base.
assert.strictEqual(question.minWidth, "", "minWidth property value is empty string");
question.resetPropertyValue("minWidth");
assert.equal(question.minWidth, "300px", "minWidth property value is reset, #2");
});
QUnit.test("base.resetPropertyValue() for localization string", function (assert) {
const survey = new SurveyModel();
assert.equal(survey.completeText, "Complete", "default value");
survey.completeText = "test";
assert.equal(survey.completeText, "test", "set value");
survey.resetPropertyValue("completeText");
assert.equal(survey.completeText, "Complete", "default value, #2");
const prop = Serializer.findProperty("survey", "completeText");
prop.setValue(survey, "", null);
assert.equal(survey.completeText, "", "Empty string after prop.setValue func");
});
34 changes: 34 additions & 0 deletions tests/editingObjectTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1438,3 +1438,37 @@ QUnit.test("Check column min width property is set correctly to editor", functio
property.onPropertyEditorUpdate(column, editor);
assert.equal(editor.value, "");
});
QUnit.test("Allow to set empty string into localization string & string property with default value", function (assert) {
const question = new QuestionDropdownModel("q1");
const survey = new SurveyModel({
elements: [
{ type: "text", name: "placeholder" },
{ type: "text", name: "minWidth" },
{ type: "text", name: "valueName" },
],
});
survey.editingObj = question;
const placeholderQuestion = survey.getQuestionByName("placeholder");
const minWidthQuestion = survey.getQuestionByName("minWidth");
const valueNameQuestion = survey.getQuestionByName("valueName");
assert.equal(placeholderQuestion.value, "Select...", "placeholder - default value");
assert.equal(minWidthQuestion.value, "300px", "minWidth - default value");
assert.notOk(valueNameQuestion.value, "valueName is empty by default");
placeholderQuestion.value = "";
minWidthQuestion.value = "";
valueNameQuestion.value = "";
assert.strictEqual(placeholderQuestion.value, "", "placeholder question value is empty");
assert.strictEqual(minWidthQuestion.value, "", "minWidth question value is empty");
assert.strictEqual(question.placeholder, "", "dropdown.placeholder value is empty");
assert.strictEqual(question.minWidth, "", "dropdown.minWidth value is empty");
assert.deepEqual(question.toJSON(), {
name: "q1", placeholder: "", minWidth: ""
});
const q2 = Serializer.createClass("dropdown");
q2.fromJSON({
name: "q2", placeholder: "", minWidth: ""
});
assert.equal(q2.name, "q2", "set the name correctly");
assert.strictEqual(q2.placeholder, "", "q2.placeholder value is empty");
assert.strictEqual(q2.minWidth, "", "q2.minWidth value is empty");
});
20 changes: 17 additions & 3 deletions tests/localizablestringtests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import {
LocalizableString,
LocalizableStrings,
} from "../src/localizablestring";
import { JsonObject, propertyArray, Serializer } from "../src/jsonobject";
import { JsonObject, Serializer } from "../src/jsonobject";
import { ItemValue } from "../src/itemvalue";
import { HashTable } from "../src/helpers";
import { settings } from "../src/settings";
import { surveyLocalization } from "../src/surveyStrings";
import { englishStrings } from "../src/localization/english";
import { Base } from "../src/base";
import { Question } from "../src/question";
import { SurveyElementCore } from "../src/survey-element";

export default QUnit.module("LocalizableString");

Expand Down Expand Up @@ -810,3 +808,19 @@ QUnit.test("Support disableLocalization", function(assert) {
locString.setJson("default-2");
assert.deepEqual(locString.getJson(), "default-2", "#4");
});
QUnit.test("Allow to set empty string if there is localization name", function(assert) {
const owner = new LocalizableOwnerTester("");
const locString = new LocalizableString(owner, true);
locString.localizationName = "completeText";
assert.equal(locString.renderedHtml, "Complete", "get value from localizationName, renderedHtml");
assert.equal(locString.text, "Complete", "get value from localizationName, text");
locString.text = "";
assert.equal(locString.renderedHtml, "", "empty rendred html");
assert.equal(locString.text, "", "empty text");
assert.strictEqual(locString.getJson(), "", "Return empty string in getJson()");
const locString2 = new LocalizableString(owner, true);
locString2.localizationName = "completeText";
assert.equal(locString2.renderedHtml, "Complete", "locString2 = default value, renderedHtml");
locString2.setJson("");
assert.equal(locString2.renderedHtml, "", "locString2 = empty string after setJson, renderedHtml");
});
34 changes: 34 additions & 0 deletions tests/surveyserializationtests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,4 +715,38 @@ QUnit.test("choiceValuesFromQuestion properties visibility", function (assert) {
assert.equal(propMode.visibleIf(col2), false, "col2.choicesFromQuestionMode");
assert.equal(propValues.visibleIf(col2), true, "col2.choiceValuesFromQuestion");
assert.equal(propTexts.visibleIf(col2), true, "col2.choiceTextsFromQuestion");
});
QUnit.test("Allow to save empty string for localization strings", function (assert) {
const survey = new SurveyModel({
questions: [
{ name: "q1", type: "dropdown", choices: [1, 2, 3] }
]
});
const q1 = <QuestionDropdownModel>survey.getQuestionByName("q1");
assert.equal(q1.placeholder, "Select...", "Default string for placeholder");
q1.placeholder = "test";
assert.equal(q1.placeholder, "test", "set value for placeholder");
q1.placeholder = "";
assert.equal(q1.placeholder, "", "set empty string for placeholder");
assert.strictEqual(q1.locPlaceholder.getJson(), "", "JSON has empty string");
q1.placeholder = "test";
assert.equal(q1.placeholder, "test", "set value for placeholder, #2");
q1.locPlaceholder.clear();
assert.equal(q1.placeholder, "Select...", "Clear value for placeholder");
q1.placeholder = "test";
assert.equal(q1.placeholder, "test", "set value for placeholder, #3");
q1.locPlaceholder.clearLocale();
assert.equal(q1.placeholder, "Select...", "ClearLocale for placeholder");
});
QUnit.test("Allow to save empty string for trings with default value", function (assert) {
const q = new QuestionTextModel("q1");
assert.equal(q.minWidth, "300px", "Default value is 300px");
q.minWidth = "";
assert.equal(q.minWidth, "", "set empty width");
const json = q.toJSON();
assert.deepEqual(json, { name: "q1", minWidth: "" }, "Serialize empty minWidth");
q.setPropertyValue("minWidth", undefined);
assert.equal(q.minWidth, "300px", "Default value again");
q.fromJSON({ name: "q1", minWidth: "" });
assert.equal(q.minWidth, "", "empty width was in JSON");
});

0 comments on commit cd15077

Please sign in to comment.