PromucFlow_constructor/app/client/src/constants/PropertyControlConstants.tsx
Pawan Kumar 9ffcb64da9
chore: Add documentation for property pane config (#36880)
/ok-to-test tags="@tag.Anvil"

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a new `PropertyControlFactory` class for managing property
controls.
- Added new properties to the `PropertyPaneControlConfig` interface,
enhancing customization and validation options.
- Implemented methods for creating and registering control builders,
improving the flexibility of property controls.

- **Documentation**
- Enhanced JSDoc comments for existing properties and methods, providing
clearer guidance on their usage.
- Updated documentation for the `PropertyPaneSectionConfig`,
`PropertyPaneControlConfig`, and `ValidationConfigParams` interfaces to
clarify their functionality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/11716829822>
> Commit: 62df82621a44126f2d876c77a892010265ec53d3
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11716829822&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Anvil`
> Spec:
> <hr>Thu, 07 Nov 2024 05:15:44 UTC
<!-- end of auto-generated comment: Cypress test results  -->
2024-11-07 15:41:20 +05:30

436 lines
14 KiB
TypeScript

import { getPropertyControlTypes } from "components/propertyControls";
import type {
ValidationResponse,
ValidationTypes,
} from "constants/WidgetValidation";
import type { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
import type { CodeEditorExpected } from "components/editorComponents/CodeEditor";
import type { UpdateWidgetPropertyPayload } from "actions/controlActions";
import type { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator";
import type { Stylesheet } from "entities/AppTheming";
import type { ReduxActionType } from "ee/constants/ReduxActionConstants";
import type { PropertyUpdates } from "WidgetProvider/constants";
import type { WidgetProps } from "widgets/BaseWidget";
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[];
/**
* 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;
/**
* 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[];
expandedByDefault?: boolean;
}
export interface PanelConfig {
editableTitle: boolean;
titlePropertyName: string;
panelIdPropertyName: string;
children?: PropertyPaneConfig[];
contentChildren?: PropertyPaneConfig[];
styleChildren?: PropertyPaneConfig[];
searchConfig?: PropertyPaneConfig[]; // A combination of contentChildren and contentChildren which will be used to display search results
updateHook: (
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
props: any,
propertyPath: string,
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
propertyValue: any,
) => Array<PropertyUpdates> | undefined;
}
export interface PropertyPaneControlConfig {
/**
* Unique identifier for the control. Used for internal tracking and debugging.
* It added by `addPropertyConfigIds` function in `WidgetProvider/factory/helpers.ts`.
*/
id?: string;
/**
* Label shown above the control.
*/
label: string;
/**
* Human-readable slugified name of the property. Used to identify the property in the widget properties.
*/
propertyName: string;
/**
* 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 that appears below the property input.
*/
helperText?: ((props: unknown) => React.ReactNode) | React.ReactNode;
/**
* 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;
/**
* 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
// eslint-disable-next-line @typescript-eslint/no-explicit-any
propertyValue: any,
// TODO: Fix this the next time the file is edited
// 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
*
* @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
props: any,
propertyName: string,
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
propertyValue: any,
) => Array<PropertyUpdates> | 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;
/**
* 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 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[];
/**
* 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 )
*
* @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
props: any,
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
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options?: any;
// The following should ideally be used internally
postUpdateAction?: ReduxActionType;
/**
* If true, the property is a panel property ( that is nested property pane )
*/
isPanelProperty?: boolean;
// 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<string, unknown>;
/**
* The default value of the property.
*/
defaultValue?: unknown;
/**
* If the property is marked reusable, on the next drop it will use the value of the last dropped widget.
*/
isReusable?: boolean;
}
interface ValidationConfigParams {
/**
* 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<{
name: string;
type: ValidationTypes;
params?: ValidationConfigParams;
}>;
/**
* 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
// eslint-disable-next-line @typescript-eslint/no-explicit-any
props: any,
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_?: any,
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
moment?: any,
) => 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 {
type: ValidationTypes;
params?: ValidationConfigParams;
}
export type PropertyPaneConfig =
| PropertyPaneSectionConfig
| PropertyPaneControlConfig;
export interface ActionValidationConfigMap {
[configProperty: string]: ValidationConfig;
}