diff --git a/docs/customize-question-types-create-composite-question-types.md b/docs/customize-question-types-create-composite-question-types.md index 9a0c9f5274..6f79ccd353 100644 --- a/docs/customize-question-types-create-composite-question-types.md +++ b/docs/customize-question-types-create-composite-question-types.md @@ -23,7 +23,9 @@ Survey.ComponentCollection.Instance.add({ // A unique name; must use lowercase name: "fullname", // A display name used in the Toolbox - title: "Full Name", + title: "Full Name", + // A default title for questions created with this question type + defaultQuestionTitle: "Enter your full name:", // An array of JSON schemas that configure the nested questions elementsJSON: [ { type: "text", name: "firstName", title: "First Name", isRequired: true }, @@ -64,6 +66,7 @@ For example, the Full Name composite question from the previous topic may includ Survey.ComponentCollection.Instance.add({ name: "fullname", title: "Full Name", + defaultQuestionTitle: "Enter your full name:", elementsJSON: [ { type: "text", name: "firstName", title: "First Name", isRequired: true }, // Optional question, hidden by default diff --git a/docs/customize-question-types-create-specialized-question-types.md b/docs/customize-question-types-create-specialized-question-types.md index 73f2cca446..21c74323b4 100644 --- a/docs/customize-question-types-create-specialized-question-types.md +++ b/docs/customize-question-types-create-specialized-question-types.md @@ -34,6 +34,8 @@ Survey.ComponentCollection.Instance.add({ name: "country", // A display name used in the Toolbox title: "Country", + // A default title for questions created with this question type + defaultQuestionTitle: "Country", // A JSON schema for the base question type (Dropdown in this case) questionJSON: { "type": "dropdown", diff --git a/src/question_custom.ts b/src/question_custom.ts index 3dc91c5289..622db43a84 100644 --- a/src/question_custom.ts +++ b/src/question_custom.ts @@ -16,6 +16,7 @@ import { Helpers, HashTable } from "./helpers"; import { ItemValue } from "./itemvalue"; import { QuestionTextProcessor } from "./textPreProcessor"; import { CssClassBuilder } from "./utils/cssClassBuilder"; +import { LocalizableString } from "./localizablestring"; /** * An interface used to create custom question types. @@ -50,13 +51,37 @@ export interface ICustomQuestionTypeConfiguration { */ onInit?(): void; /** - * Specifies whether the custom question type is available in the Toolbox and the Add Question menu. + * Specifies whether the custom question type is available in the Toolbox and the Add Question menu in Survey Creator. * * Default value: `true` * * Set this property to `false` if your custom question type is used only to customize Property Grid content and is not meant for a survey. */ showInToolbox?: boolean; + /** + * A default title for questions created with this question type. Survey authors can change the default title in the JSON object or in Survey Creator's Property Grid. + * + * You can specify the question title with a string value or with an object that defines different titles for different locales: + * + * ```js + * import { ComponentCollection } from "survey-core"; + * + * ComponentCollection.Instance.add({ + * // ... + * defaultQuestionTitle: "Default title" + * }); + * // ===== OR ===== + * ComponentCollection.Instance.add({ + * // ... + * defaultQuestionTitle: { + * en: "Default title", + * de: "Standardtitel", + * fr: "Titre par défaut" + * } + * }); + * ``` + */ + defaultQuestionTitle?: any; /** * A function that is called when the custom question is created. Use it to access questions nested within a [composite question type](https://surveyjs.io/form-library/documentation/customize-question-types/create-composite-question-types). * @@ -292,6 +317,9 @@ export class ComponentQuestionJSON { if (!this.json.getDisplayValue) return question.getDisplayValue(keyAsText, value); return (this.json as any).getDisplayValue(question); } + public get defaultQuestionTitle(): any { + return this.json.defaultQuestionTitle; + } public setValueToQuestion(val: any): any { const converter = this.json.valueToQuestion || this.json.setValue; return !!converter ? converter(val): val; @@ -395,10 +423,13 @@ export class ComponentCollection { export abstract class QuestionCustomModelBase extends Question implements ISurveyImpl, ISurveyData, IPanel { + private locQuestionTitle: LocalizableString; constructor(name: string, public customQuestion: ComponentQuestionJSON) { super(name); CustomPropertiesCollection.createProperties(this); SurveyElement.CreateDisabledDesignElements = true; + this.locQuestionTitle = this.createLocalizableString("questionTitle", this); + this.locQuestionTitle.setJson(this.customQuestion.defaultQuestionTitle); this.createWrapper(); SurveyElement.CreateDisabledDesignElements = false; if (!!this.customQuestion) { @@ -420,6 +451,12 @@ export abstract class QuestionCustomModelBase extends Question this.getElement().localeChanged(); } } + protected getDefaultTitle(): string { + if(!this.locQuestionTitle.isEmpty) { + return this.getProcessedText(this.locQuestionTitle.textOrHtml); + } + return super.getDefaultTitle(); + } public addUsedLocales(locales: Array): void { super.addUsedLocales(locales); if(!!this.getElement()) { diff --git a/tests/question_customtests.ts b/tests/question_customtests.ts index 9a2b1a5d02..84e7f47899 100644 --- a/tests/question_customtests.ts +++ b/tests/question_customtests.ts @@ -2680,3 +2680,53 @@ QUnit.test("showPreview & default value, #7640", function (assert) { ComponentCollection.Instance.clear(); }); +QUnit.test("single component: defaultQuestionTitle", function (assert) { + ComponentCollection.Instance.add({ + name: "customtext", + defaultQuestionTitle: { + en: "abc={abc} en", + de: "abc={abc} de", + }, + questionJSON: { + type: "text" + }, + }); + + const survey = new SurveyModel({ + elements: [ + { type: "customtext", name: "q1" } + ] + }); + const q1 = survey.getQuestionByName("q1"); + survey.setVariable("abc", 123); + assert.equal(q1.locTitle.renderedHtml, "abc=123 en", "q1.title en"); + survey.locale = "de"; + assert.equal(q1.locTitle.renderedHtml, "abc=123 de", "q1.title de"); + + ComponentCollection.Instance.clear(); +}); +QUnit.test("composite component: defaultQuestionTitle", function (assert) { + ComponentCollection.Instance.add({ + name: "customtext", + defaultQuestionTitle: { + en: "abc={abc} en", + de: "abc={abc} de", + }, + elementsJSON: { + type: "text" + }, + }); + + const survey = new SurveyModel({ + elements: [ + { type: "customtext", name: "q1" } + ] + }); + const q1 = survey.getQuestionByName("q1"); + survey.setVariable("abc", 123); + assert.equal(q1.locTitle.renderedHtml, "abc=123 en", "q1.title en"); + survey.locale = "de"; + assert.equal(q1.locTitle.renderedHtml, "abc=123 de", "q1.title de"); + + ComponentCollection.Instance.clear(); +});