diff --git a/packages/survey-creator-core/src/question-editor/properties.ts b/packages/survey-creator-core/src/question-editor/properties.ts index 6b1f1782e5..057e30b3a8 100644 --- a/packages/survey-creator-core/src/question-editor/properties.ts +++ b/packages/survey-creator-core/src/question-editor/properties.ts @@ -2,12 +2,12 @@ import { SurveyQuestionEditorDefinition, ISurveyQuestionEditorDefinition, } from "./definition"; -import * as Survey from "survey-core"; +import { JsonObjectProperty, Serializer, JsonMetadataClass } from "survey-core"; import { SurveyHelper } from "../survey-helper"; import { ISurveyCreatorOptions, settings } from "../creator-settings"; export class SurveyQuestionEditorPropertyDefinition { - public property: Survey.JsonObjectProperty; + public property: JsonObjectProperty; public title: string; public category: string; public createdFromTabName: boolean; @@ -29,7 +29,7 @@ const otherTabName = "others"; export class SurveyQuestionProperties { private showModeValue: string; - private properties: Array; + private properties: Array; private propertiesHash: any; private tabs: Array = []; constructor( @@ -38,14 +38,14 @@ export class SurveyQuestionProperties { className: string = null, showMode: string = null, private parentObj: any = null, - private parentProperty: Survey.JsonObjectProperty = null + private parentProperty: JsonObjectProperty = null ) { this.showModeValue = showMode; - this.properties = Survey.Serializer.getPropertiesByObj(this.obj); + this.properties = Serializer.getPropertiesByObj(this.obj); this.fillPropertiesHash(); this.buildTabs(className); } - public getProperty(propertyName: string): Survey.JsonObjectProperty { + public getProperty(propertyName: string): JsonObjectProperty { var res = this.propertiesHash[propertyName]; return !!res && res.visible ? res.property : null; } @@ -76,7 +76,7 @@ export class SurveyQuestionProperties { }; } } - private isJSONPropertyVisible(property: Survey.JsonObjectProperty): boolean { + private isJSONPropertyVisible(property: JsonObjectProperty): boolean { var res = this.propertiesHash[property.name]; return !!res && res.visible; } @@ -91,7 +91,7 @@ export class SurveyQuestionProperties { } public getProperties( tab: SurveyQuestionEditorTabDefinition - ): Array { + ): Array { var res = []; for (var i = 0; i < tab.properties.length; i++) { res.push(tab.properties[i].property); @@ -208,7 +208,7 @@ export class SurveyQuestionProperties { } return null; } - private getNextToNameProperty(property: Survey.JsonObjectProperty): string { + private getNextToNameProperty(property: JsonObjectProperty): string { if(!property.nextToProperty) return ""; if(this.isPropertyOnSameLine(property.nextToProperty)) return property.nextToProperty.substring(1); return property.nextToProperty; @@ -270,55 +270,66 @@ export class SurveyQuestionProperties { this.addNonTabProperties(result, usedProperties, true); return result; } - var curClassName = className; - var hasNonTabProperties = false; + let hasNonTabProperties = this.getAllDefinitionsByClassCore(className, usedProperties, result); + const dynamicClass = this.obj.isQuestion && !!this.obj.getDynamicType ? this.obj.getDynamicType() : ""; + if(dynamicClass) { + hasNonTabProperties = this.getAllDefinitionsByClassCore(dynamicClass, usedProperties, result); + } + + if (!hasNonTabProperties) { + this.addNonTabProperties(result, usedProperties); + } + return result; + } + private getAllDefinitionsByClassCore(className: string, usedProperties: any, result: Array): boolean { + let res = false; + let curClassName = className; while (curClassName) { - let metaClass = ( - Survey.Serializer.findClass(curClassName) + let metaClass = ( + Serializer.findClass(curClassName) ); if (!metaClass) break; - let classRes = SurveyQuestionEditorDefinition.definition[metaClass.name]; - if (classRes) { - if (classRes.properties) { - var i = 0; - while (i < classRes.properties.length) { - var prop = classRes.properties[i]; - var propName = typeof prop == "string" ? prop : prop.name; - var tabName = settings.propertyGrid.generalTabName; - if (typeof prop !== "string" && !!prop.tab) { - tabName = prop.tab; - } - var jsonProp = !!this.propertiesHash[propName] - ? this.propertiesHash[propName].property - : null; - var jsonPropertyCategory = this.getJsonPropertyCategory(jsonProp); - if (!!jsonPropertyCategory && jsonPropertyCategory !== tabName) { - classRes.properties.splice(i, 1); - } else { - usedProperties[propName] = true; - i++; - } - } + res = this.getAllDefinitionsByClassSingleCore(metaClass.name, usedProperties, result); + curClassName = metaClass.parentName; + } + return res; + } + private getAllDefinitionsByClassSingleCore(className: string, usedProperties: any, result: Array): boolean { + const classRes = SurveyQuestionEditorDefinition.definition[className]; + let res = false; + if(!classRes) return res; + if (classRes.properties) { + var i = 0; + while (i < classRes.properties.length) { + var prop = classRes.properties[i]; + var propName = typeof prop == "string" ? prop : prop.name; + var tabName = settings.propertyGrid.generalTabName; + if (typeof prop !== "string" && !!prop.tab) { + tabName = prop.tab; } - if (classRes.tabs) { - for (var i = 0; i < classRes.tabs.length; i++) { - hasNonTabProperties = - hasNonTabProperties || classRes.tabs[i].name === otherTabName; - usedProperties[classRes.tabs[i].name] = true; - } + var jsonProp = !!this.propertiesHash[propName] + ? this.propertiesHash[propName].property + : null; + var jsonPropertyCategory = this.getJsonPropertyCategory(jsonProp); + if (!!jsonPropertyCategory && jsonPropertyCategory !== tabName) { + classRes.properties.splice(i, 1); + } else { + usedProperties[propName] = true; + i++; } - result.unshift(classRes); } - curClassName = metaClass.parentName; } - - if (!hasNonTabProperties) { - this.addNonTabProperties(result, usedProperties); + if (classRes.tabs) { + for (var i = 0; i < classRes.tabs.length; i++) { + res = res || classRes.tabs[i].name === otherTabName; + usedProperties[classRes.tabs[i].name] = true; + } } - return result; + result.unshift(classRes); + return res; } private getJsonPropertyCategory( - jsonProperty: Survey.JsonObjectProperty + jsonProperty: JsonObjectProperty ): string { if (!jsonProperty) return null; if (!!jsonProperty.category) return jsonProperty.category; diff --git a/packages/survey-creator-core/tests/property-grid/property-grid.tests.ts b/packages/survey-creator-core/tests/property-grid/property-grid.tests.ts index 2015f2040a..84f0ef3869 100644 --- a/packages/survey-creator-core/tests/property-grid/property-grid.tests.ts +++ b/packages/survey-creator-core/tests/property-grid/property-grid.tests.ts @@ -29,7 +29,8 @@ import { surveyLocalization, AdaptiveActionContainer, QuestionCommentModel, - QuestionImagePickerModel + QuestionImagePickerModel, + ComponentCollection } from "survey-core"; import { EmptySurveyCreatorOptions, @@ -2949,3 +2950,29 @@ test("Allow delete all pages by default", () => { expect(pagesQuestion.canRemoveRow(pagesQuestion.visibleRows[0])).toBeTruthy(); expect(pagesQuestion.canRemoveRow(pagesQuestion.visibleRows[1])).toBeTruthy(); }); +test("Setup correct categories for dynamic properties in components", () => { + ComponentCollection.Instance.add({ + name: "customdropdown", + inheritBaseProps: ["allowClear", "showOtherItem"], + questionJSON: { + type: "dropdown", + choices: [1, 2, 3] + }, + }); + const survey = new SurveyModel(); + survey.setDesignMode(true); + survey.fromJSON({ + elements: [ + { type: "customdropdown", name: "q1", allowClear: false, showOtherItem: true } + ] + }); + const question = survey.getQuestionByName("q1"); + var propertyGrid = new PropertyGridModelTester(question); + const visibleQuestion = propertyGrid.survey.getQuestionByName("visible"); + const allowClearQuestion = propertyGrid.survey.getQuestionByName("allowClear"); + const showOtherItemQuestion = propertyGrid.survey.getQuestionByName("showOtherItem"); + expect(visibleQuestion.parent.name).toBe("general"); + expect(allowClearQuestion.parent.name).toBe("choices"); + expect(showOtherItemQuestion.parent.name).toBe("choices"); + ComponentCollection.Instance.clear(); +});