diff --git a/app/client/src/constants/PropertyControlConstants.tsx b/app/client/src/constants/PropertyControlConstants.tsx index 0d52e03366dc..a0e25666b543 100644 --- a/app/client/src/constants/PropertyControlConstants.tsx +++ b/app/client/src/constants/PropertyControlConstants.tsx @@ -17,19 +17,73 @@ const ControlTypes = getPropertyControlTypes(); export type ControlType = (typeof ControlTypes)[keyof typeof ControlTypes]; export interface PropertyPaneSectionConfig { + /** + * Title displayed at the top of a collapsible section in the property pane. + */ sectionName: string; + /** + * Unique identifier for the section. Used for: + * - Managing section collapse/expand state in Redux + * - Navigation and search functionality in the property pane + * - React rendering optimization (as key) + * + * If not provided, it will be auto-generated during widget initialization. + */ id?: string; + /** + * Array of properties that should go inside the section. + */ children: PropertyPaneConfig[]; - collapsible?: boolean; // Indicates whether the section could be collapsed or not - childrenId?: string; // A unique id generated by combining the ids of all the children + /** + * If false, the section will not be collapsible. + */ + collapsible?: boolean; + /** + * A unique id generated by combining the ids of all the children. + * Mainly used in memoization to prevent unnecessary re-renders of property sections ( PropertySection.tsx ) + */ + childrenId?: string; + /** + * Callback function to determine if the section should be hidden. + * + * @param props - Current widget properties + * @param propertyPath - Path to the widget property + * @returns - True if the section should be hidden, false otherwise + */ // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any hidden?: (props: any, propertyPath: string) => boolean; + /** + * when true, the section will be open by default. + * Note: Seems like this is not used anywhere. + */ isDefaultOpen?: boolean; propertySectionPath?: string; - tag?: string; // Used to show a tag right after the section name (only in the search results) - + /** + * Used to show a tag right after the section name (usedonly in the search results) + */ + tag?: string; + /** + * Indicates whether this section contains properties that need to be generated dynamically at runtime. + * Used in conjunction with generateDynamicProperties. + * + * Common use cases: + * - Dynamic event handlers ( CustomWidget's ) + * - Properties that depend on user configuration + * - Properties that need to be generated based on widget state + */ hasDynamicProperties?: boolean; + /** + * Function to generate property controls dynamically at runtime. + * Called when hasDynamicProperties is true. + * + * @param widget - Current widget properties + * @returns Array of dynamically generated property controls + * + * @example + * Used in CustomWidget and ExternalWidget to dynamically generate event handler properties + * based on available events. + */ generateDynamicProperties?: ( widget: WidgetProps, ) => PropertyPaneControlConfig[]; @@ -56,26 +110,64 @@ export interface PanelConfig { } export interface PropertyPaneControlConfig { - // unique id to identify the property. It is added automatically with generateReactKey() + /** + * Unique identifier for the control. Used for internal tracking and debugging. + * It added by `addPropertyConfigIds` function in `WidgetProvider/factory/helpers.ts`. + */ id?: string; - // label is used to display the name of the property + /** + * Label shown above the control. + */ label: string; - // unique name of the property + /** + * Human-readable slugified name of the property. Used to identify the property in the widget properties. + */ propertyName: string; - // Serves in the tooltip + /** + * Used to provide more context about the property. Appears as tooltip when we hover over the label of the property. + * Note: This is different from `helperText` which appears below the property input. + */ helpText?: string; - // Dynamic text serves below the property pane inputs + /** + * Dynamic text that appears below the property input. + */ helperText?: ((props: unknown) => React.ReactNode) | React.ReactNode; - // used to tell if the property is a JS convertible property. - // If true, It will show the little JS icon button next to the property name + /** + * If true, transform the property input into plain input where js can be written.. + */ isJSConvertible?: boolean; + /** + * Used in special cases where we want to custom control instead regular text input control. + * For example: `COMPUTE_VALUE` control is used in table widget provides access to currentRow in controls in the table columns. + */ customJSControl?: string; + /** + * Type of the property control. + * Full list of supported controls can be found in `app/client/src/components/propertyControls/index.ts`. + */ controlType: ControlType; + /** @deprecated. Not used anywhere. */ validationMessage?: string; + /** + * path that is used to get evaluated value for the property. + */ dataTreePath?: string; - // used to define children property configs when the current property is a section + /** + * There is no requirement to define children for a control. This is just done to suffice types + * in places where controlConfig and sectionConfig are not differentiated. + */ children?: PropertyPaneConfig[]; panelConfig?: PanelConfig; + /** + * Callback function to update related widget properties. + * + * @param propertyName - Path to the widget property + * @param propertyValue - New value of the property + * @param props - Current widget properties + * @returns - Array of property updates + * + * @example Used in tabs widget to update the label of the tab. + */ updateRelatedWidgetProperties?: ( propertyName: string, // TODO: Fix this the next time the file is edited @@ -85,7 +177,15 @@ export interface PropertyPaneControlConfig { // eslint-disable-next-line @typescript-eslint/no-explicit-any props: any, ) => UpdateWidgetPropertyPayload[]; - // Function that is called when the property is updated, it is mainly used to update other properties + /** + * Function that is called when the property is updated, it is mainly used to update other properties + * + * @param props - Current widget properties + * @param propertyName - Path to the widget property + * @param propertyValue - New value of the property + * + * @returns - Array of property updates + */ updateHook?: ( // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -95,24 +195,70 @@ export interface PropertyPaneControlConfig { // eslint-disable-next-line @typescript-eslint/no-explicit-any propertyValue: any, ) => Array | undefined; + /** + * callback function to determine if the property should be hidden. + + * @param props - Current widget properties + * @param propertyPath - Path to the widget property + * @returns - True if the property should be hidden, false otherwise + */ // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any hidden?: (props: any, propertyPath: string) => boolean; + /** + * If true, the property is hidden. + * Note: hidden and invisible do the same thing but differently. hidden uses a callback to determine if the property should be hidden. + * invisible is a boolean flag to hide the property. + */ invisible?: boolean; isBindProperty: boolean; + /** + * If true, it means the property triggers a widget action. + * + * @example + * OnClick property in Button widget is a trigger property that can trigger an action. + */ isTriggerProperty: boolean; + /** + * Validation configuration for the property + */ validation?: ValidationConfig; - useValidationMessage?: boolean; + /** + * Callback function to provide additional autocomplete data for the autocomplete list. + * + * @param props - Current widget properties + * @returns - Additional autocomplete data + */ // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any additionalAutoComplete?: (props: any) => AdditionalDynamicDataTree; evaluationSubstitutionType?: EvaluationSubstitutionType; - // all the properties that current property is dependent on. All the properties passed here comes into widgetProperties + /** + * All properties that current property is dependent on. + * All of these properties becomes available in `widgetProperties` in the property control.. + */ dependencies?: string[]; + /** + * It is same as `dependencies` but these dependencies are not statically defined in the widget. + * The callback is called during rendering of the property control to get the latest dependencies. + * + * @example + * Used in CustomWidget to dynamically get the dependencies based on the current state of the widget. + */ dynamicDependencies?: (widget: WidgetProps) => string[]; - evaluatedDependencies?: string[]; // dependencies to be picked from the __evaluated__ object + /** + * Dependencies to be picked from the __evaluated__ object + */ + evaluatedDependencies?: string[]; expected?: CodeEditorExpected; - // Used to get value of the property from stylesheet config. Used in app theming v1 ( Not needed in anvil ) + /** + * Used to get value of the property from stylesheet config. Used in app theming v1 ( Not needed in anvil ) + * + * @param props - Current widget properties + * @param propertyPath - Path to the widget property + * @param stylesheet - Stylesheet config + * @returns - Value of the property + */ getStylesheetValue?: ( // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -120,6 +266,10 @@ export interface PropertyPaneControlConfig { propertyPath: string, stylesheet?: Stylesheet, ) => Stylesheet[string]; + /** + * Options for certain controls like Dropdown Control + * Note: This should be moved to controlConfig instead of being a top level property. + */ // TODO(abhinav): To fix this, rename the options property of the controls which use this // Alternatively, create a new structure // TODO: Fix this the next time the file is edited @@ -127,47 +277,92 @@ export interface PropertyPaneControlConfig { options?: any; // The following should ideally be used internally postUpdateAction?: ReduxActionType; - onBlur?: () => void; - onFocus?: () => void; + /** + * If true, the property is a panel property ( that is nested property pane ) + */ isPanelProperty?: boolean; - // Numeric Input Control - min?: number; // Switch mode ( JS -> Text ) shouldSwitchToNormalMode?: ( isDynamic: boolean, isToggleDisabled: boolean, triggerFlag?: boolean, ) => boolean; - /** * `controlConfig` is a generic record that can be used to pass additional configuration * options to the property control. The specific structure and contents of this record * will depend on the control type and its individual requirements. */ controlConfig?: Record; + /** + * The default value of the property. + */ defaultValue?: unknown; - /** used to mark a property as reusable so that it can be reused in next dropping widget */ + /** + * If the property is marked reusable, on the next drop it will use the value of the last dropped widget. + */ isReusable?: boolean; } interface ValidationConfigParams { - min?: number; // min allowed for a number - max?: number; // max allowed for a number - natural?: boolean; // is a positive integer - default?: unknown; // default for any type - unique?: boolean | string[]; // unique in an array (string if a particular path is unique) - required?: boolean; // required type - // required is now used to check if value is an empty string. - requiredKey?: boolean; //required key - regex?: RegExp; // validator regex for text type + /** + * Minimum allowed value for a number. + */ + min?: number; + /** + * Maximum allowed value for a number. + */ + max?: number; + /** + * If true, the value must be a positive integer. + */ + natural?: boolean; + /** + * Default value for any type. + */ + default?: unknown; + /** + * If true or an array of strings, the value must be unique in an array. + * If an array of strings is provided, it specifies particular paths that must be unique. + */ + unique?: boolean | string[]; + /** + * If true, the value is required. + * Now used to check if the value is an empty string. + */ + required?: boolean; + /** + * If true, the key is required. + */ + requiredKey?: boolean; + /** + * Validator regex for text type. + */ + regex?: RegExp; + /** + * Allowed keys in an object type. + */ allowedKeys?: Array<{ - // Allowed keys in an object type name: string; type: ValidationTypes; params?: ValidationConfigParams; }>; - allowedValues?: unknown[]; // Allowed values in a string and array type - children?: ValidationConfig; // Children configurations in an ARRAY or OBJECT_ARRAY type + /** + * Allowed values in a string and array type. + */ + allowedValues?: unknown[]; + /** + * Children configurations in an ARRAY or OBJECT_ARRAY type. + */ + children?: ValidationConfig; + /** + * Validation function for FUNCTION type. + * + * @param value - The value to validate + * @param props - Current widget properties + * @param _ - Additional parameter (unused) + * @param moment - Moment.js instance + * @returns - Validation response + */ fn?: ( value: unknown, // TODO: Fix this the next time the file is edited @@ -179,18 +374,51 @@ interface ValidationConfigParams { // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any moment?: any, - ) => ValidationResponse; // Function in a FUNCTION type - fnString?: string; // AUTO GENERATED, SHOULD NOT BE SET BY WIDGET DEVELOPER - expected?: CodeEditorExpected; // FUNCTION type expected type and example - strict?: boolean; //for strict string validation of TEXT type - ignoreCase?: boolean; //to ignore the case of key - type?: ValidationTypes; // Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type - types?: ValidationConfig[]; // Used for ValidationType.UNION to define sub type - params?: ValidationConfigParams; // Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type params - passThroughOnZero?: boolean; // Used for ValidationType.NUMBER to allow 0 to be passed through. Deafults value is true - limitLineBreaks?: boolean; // Used for ValidationType.TEXT to limit line breaks in a large json object. - defaultValue?: unknown; // used for ValidationType.UNION when none the union type validation is success - defaultErrorMessage?: string; // used for ValidationType.UNION when none the union type validation is success + ) => ValidationResponse; + /** + * AUTO GENERATED, SHOULD NOT BE SET BY WIDGET DEVELOPER + */ + fnString?: string; + /** + * FUNCTION type expected type and example. + */ + expected?: CodeEditorExpected; + /** + * If true, enables strict string validation of TEXT type. + */ + strict?: boolean; + /** + * If true, ignores the case of keys. + */ + ignoreCase?: boolean; + /** + * Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type. + */ + type?: ValidationTypes; + /** + * Used for ValidationType.UNION to define sub types. + */ + types?: ValidationConfig[]; + /** + * Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type params. + */ + params?: ValidationConfigParams; + /** + * Used for ValidationType.NUMBER to allow 0 to be passed through. Default value is true. + */ + passThroughOnZero?: boolean; + /** + * Used for ValidationType.TEXT to limit line breaks in a large JSON object. + */ + limitLineBreaks?: boolean; + /** + * Used for ValidationType.UNION when none of the union type validations succeed. + */ + defaultValue?: unknown; + /** + * Used for ValidationType.UNION when none of the union type validations succeed. + */ + defaultErrorMessage?: string; } export interface ValidationConfig { diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControlsGenerator.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControlsGenerator.tsx index fa7450d33a2e..4b0452cb9b4f 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControlsGenerator.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControlsGenerator.tsx @@ -130,7 +130,7 @@ function PropertyControlsGenerator(props: PropertyControlsGeneratorProps) { ) : ( <> {generatePropertyControl( - searchResults as readonly PropertyPaneConfig[], + searchResults, props, isSearchResult, enhancements, diff --git a/app/client/src/utils/PropertyControlFactory.tsx b/app/client/src/utils/PropertyControlFactory.tsx index bb231492508a..54cc4e2fca04 100644 --- a/app/client/src/utils/PropertyControlFactory.tsx +++ b/app/client/src/utils/PropertyControlFactory.tsx @@ -9,6 +9,19 @@ import type { import type BaseControl from "components/propertyControls/BaseControl"; import { isArray } from "lodash"; import type { AdditionalDynamicDataTree } from "./autocomplete/customTreeTypeDefCreator"; + +/** + * PropertyPaneControlFactory + * + * This classes manages all the available controls for the property pane. + * It maintains a map of control types to their respective builders. + * The control builders are responsible for creating the actual controls. + * + * Key functionalities: + * 1. Register control builders, methods, and computed value functions + * 2. Create controls based on control data and preferences + * 3. Retrieve available control types + */ class PropertyControlFactory { static controlMap: Map> = new Map(); static controlMethods: Map = new Map();