In order to unify package names, we decided to use `@appsmith` prefix as a marker to indicate that packages belong to our codebase and that these packages are developed internally. So that we can use this prefix, we need to rename the alias of the same name. But since `@appsmith` is currently being used as an alias for `ee` folder, we have to rename the alias as the first step. Related discussion https://theappsmith.slack.com/archives/CPG2ZTXEY/p1722516279126329 EE PR — https://github.com/appsmithorg/appsmith-ee/pull/4801 ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/10267368821> > Commit: 2b00af2d257e4d4304db0a80072afef7513de6be > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10267368821&attempt=2" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Tue, 06 Aug 2024 14:24:22 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No
253 lines
8.8 KiB
TypeScript
253 lines
8.8 KiB
TypeScript
import type { AppState } from "ee/reducers";
|
|
import type { WidgetType } from "constants/WidgetConstants";
|
|
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
|
import { get, set } from "lodash";
|
|
import { useSelector } from "react-redux";
|
|
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
|
import { LayoutSystemTypes } from "layoutSystems/types";
|
|
import { select } from "redux-saga/effects";
|
|
import WidgetFactory from "WidgetProvider/factory";
|
|
import { getWidgets } from "./selectors";
|
|
|
|
/*
|
|
TODO(abhinav/pawan): Write unit tests for the following functions
|
|
Note:
|
|
Signature for enhancements in WidgetConfigResponse is as follows:
|
|
enhancements: {
|
|
child: {
|
|
autocomplete: (parentProps: any) => Record<string, Record<string, unknown>>,
|
|
customJSControl: (parentProps: any) => string,
|
|
propertyUpdateHook: (parentProps: any, widgetName: string, propertyPath: string, propertyValue: string),
|
|
action: (parentProps: any, dynamicString: string, responseData?: any[]) => { actionString: string, dataToApply?: any[]},
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Enum which identifies the path in the enhancements for the
|
|
export enum WidgetEnhancementType {
|
|
WIDGET_ACTION = "child.action",
|
|
PROPERTY_UPDATE = "child.propertyUpdateHook",
|
|
CUSTOM_CONTROL = "child.customJSControl",
|
|
AUTOCOMPLETE = "child.autocomplete",
|
|
HIDE_EVALUATED_VALUE = "child.hideEvaluatedValue",
|
|
UPDATE_DATA_TREE_PATH = "child.updateDataTreePath",
|
|
SHOULD_HIDE_PROPERTY = "child.shouldHideProperty",
|
|
}
|
|
|
|
export function getParentWithEnhancementFn(
|
|
widgetId: string | undefined,
|
|
widgets: CanvasWidgetsReduxState,
|
|
) {
|
|
let widget = get(widgets, widgetId || "", undefined);
|
|
|
|
// While this widget has a parent
|
|
while (widget?.parentId) {
|
|
// Get parent widget props
|
|
const parent = get(widgets, widget.parentId, undefined);
|
|
|
|
// If parent has enhancements property
|
|
// enhancements property is a new widget property which tells us that
|
|
// the property pane, properties or actions of this widget or its children
|
|
// can be enhanced
|
|
|
|
if (parent && parent.enhancements) {
|
|
return parent;
|
|
}
|
|
// If we didn't find any enhancements
|
|
// keep walking up the tree to find the parent which does
|
|
// if the parent doesn't have a parent stop walking the tree.
|
|
// also stop if the parent is the main container (Main container doesn't have enhancements)
|
|
if (parent?.parentId && parent.parentId !== MAIN_CONTAINER_WIDGET_ID) {
|
|
widget = get(widgets, widget.parentId, undefined);
|
|
|
|
continue;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
const fixedLayoutOnlyProperties = ["dynamicHeight"];
|
|
|
|
export function layoutSystemBasedPropertyFilter(
|
|
// TODO: Fix this the next time the file is edited
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
parentProps: any,
|
|
propertyName: string,
|
|
) {
|
|
return (
|
|
parentProps.layoutSystemType !== LayoutSystemTypes.FIXED &&
|
|
fixedLayoutOnlyProperties.includes(propertyName)
|
|
);
|
|
}
|
|
|
|
export function getWidgetEnhancementFn(
|
|
type: WidgetType,
|
|
enhancementType: WidgetEnhancementType,
|
|
) {
|
|
// Get enhancements for the widget type from the config response
|
|
// Spread the config response so that we don't pollute the original
|
|
// configs
|
|
|
|
const config = { ...WidgetFactory.widgetConfigMap.get(type) };
|
|
if (config?.enhancements)
|
|
return get(config.enhancements, enhancementType, undefined);
|
|
}
|
|
|
|
// TODO(abhinav): Getting data from the tree may not be needed
|
|
// confirm this.
|
|
export const getPropsFromTree = (
|
|
state: AppState,
|
|
widgetName?: string,
|
|
): unknown => {
|
|
// Get the evaluated data of this widget from the evaluations tree.
|
|
if (!widgetName) return;
|
|
|
|
return get(state.evaluations.tree, widgetName, undefined);
|
|
};
|
|
|
|
export function* getChildWidgetEnhancementFn(
|
|
widgetId: string,
|
|
enhancementType: WidgetEnhancementType,
|
|
) {
|
|
// Get all widgets from the canvas
|
|
const widgets: CanvasWidgetsReduxState = yield select(getWidgets);
|
|
// Get the parent which wants to enhance this widget
|
|
const parentWithEnhancementFn = getParentWithEnhancementFn(widgetId, widgets);
|
|
// If such a parent is found
|
|
if (parentWithEnhancementFn) {
|
|
// Get the enhancement function based on the enhancementType
|
|
// from the configs
|
|
const enhancementFn = getWidgetEnhancementFn(
|
|
parentWithEnhancementFn.type,
|
|
enhancementType,
|
|
);
|
|
// Get the parent's evaluated data from the evaluatedTree
|
|
const parentDataFromDataTree: unknown = yield select(
|
|
getPropsFromTree,
|
|
parentWithEnhancementFn.widgetName,
|
|
);
|
|
if (parentDataFromDataTree) {
|
|
// Update the enhancement function by passing the widget data as the first parameter
|
|
return (...args: unknown[]) =>
|
|
(enhancementFn as EnhancementFn)(parentDataFromDataTree, ...args);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* hook that returns parent with enhancments
|
|
*
|
|
* @param widgetId
|
|
* @returns
|
|
*/
|
|
export function useParentWithEnhancementFn(widgetId: string) {
|
|
const widgets: CanvasWidgetsReduxState = useSelector(getWidgets);
|
|
return getParentWithEnhancementFn(widgetId, widgets);
|
|
}
|
|
|
|
export function useChildWidgetEnhancementFn(
|
|
widgetId: string,
|
|
enhancementType: WidgetEnhancementType,
|
|
) {
|
|
// Get all widgets from the canvas
|
|
const widgets: CanvasWidgetsReduxState = useSelector(getWidgets);
|
|
// Get the parent which wants to enhance this widget
|
|
const parentWithEnhancementFn = getParentWithEnhancementFn(widgetId, widgets);
|
|
// If such a parent is found
|
|
// Get the parent's evaluated data from the evaluatedTree
|
|
const parentDataFromDataTree: unknown = useSelector((state: AppState) =>
|
|
getPropsFromTree(state, parentWithEnhancementFn?.widgetName),
|
|
);
|
|
|
|
if (parentWithEnhancementFn) {
|
|
// Get the enhancement function based on the enhancementType
|
|
// from the configs
|
|
const enhancementFn = getWidgetEnhancementFn(
|
|
parentWithEnhancementFn.type,
|
|
enhancementType,
|
|
);
|
|
|
|
if (parentDataFromDataTree && enhancementFn) {
|
|
// Update the enhancement function by passing the widget data as the first parameter
|
|
return (...args: unknown[]) =>
|
|
(enhancementFn as EnhancementFn)(parentDataFromDataTree, ...args);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Todo (abhinav): Specify styles here
|
|
// TODO: Fix this the next time the file is edited
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
type EnhancementFn = (parentProps: any, ...rest: any) => unknown;
|
|
// TODO: Fix this the next time the file is edited
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
type BoundEnhancementFn = (...rest: any) => unknown;
|
|
|
|
interface EnhancementFns {
|
|
updateDataTreePathFn?: BoundEnhancementFn;
|
|
propertyPaneEnhancementFn?: BoundEnhancementFn;
|
|
autoCompleteEnhancementFn?: BoundEnhancementFn;
|
|
customJSControlEnhancementFn?: BoundEnhancementFn;
|
|
hideEvaluatedValueEnhancementFn?: BoundEnhancementFn;
|
|
}
|
|
|
|
export function useChildWidgetEnhancementFns(widgetId: string): EnhancementFns {
|
|
const enhancementFns = {
|
|
updateDataTreePathFn: undefined,
|
|
propertyPaneEnhancementFn: undefined,
|
|
autoCompleteEnhancementFn: undefined,
|
|
customJSControlEnhancementFn: undefined,
|
|
hideEvaluatedValueEnhancementFn: undefined,
|
|
};
|
|
|
|
// Get all widgets from the canvas
|
|
const widgets: CanvasWidgetsReduxState = useSelector(getWidgets);
|
|
// Get the parent which wants to enhance this widget
|
|
const parentWithEnhancementFn = getParentWithEnhancementFn(widgetId, widgets);
|
|
// If such a parent is found
|
|
// Get the parent's evaluated data from the evaluatedTree
|
|
const parentDataFromDataTree: unknown = useSelector((state: AppState) =>
|
|
getPropsFromTree(state, parentWithEnhancementFn?.widgetName),
|
|
);
|
|
|
|
if (parentWithEnhancementFn) {
|
|
// Get the enhancement function based on the enhancementType
|
|
// from the configs
|
|
const widgetEnhancementFns = {
|
|
updateDataTreePathFn: getWidgetEnhancementFn(
|
|
parentWithEnhancementFn.type,
|
|
WidgetEnhancementType.UPDATE_DATA_TREE_PATH,
|
|
),
|
|
propertyPaneEnhancementFn: getWidgetEnhancementFn(
|
|
parentWithEnhancementFn.type,
|
|
WidgetEnhancementType.PROPERTY_UPDATE,
|
|
),
|
|
autoCompleteEnhancementFn: getWidgetEnhancementFn(
|
|
parentWithEnhancementFn.type,
|
|
WidgetEnhancementType.AUTOCOMPLETE,
|
|
),
|
|
customJSControlEnhancementFn: getWidgetEnhancementFn(
|
|
parentWithEnhancementFn.type,
|
|
WidgetEnhancementType.CUSTOM_CONTROL,
|
|
),
|
|
hideEvaluatedValueEnhancementFn: getWidgetEnhancementFn(
|
|
parentWithEnhancementFn.type,
|
|
WidgetEnhancementType.HIDE_EVALUATED_VALUE,
|
|
),
|
|
};
|
|
|
|
Object.keys(widgetEnhancementFns).map((key: string) => {
|
|
const enhancementFn = get(widgetEnhancementFns, `${key}`);
|
|
|
|
if (parentDataFromDataTree && enhancementFn) {
|
|
set(enhancementFns, `${key}`, (...args: unknown[]) =>
|
|
enhancementFn(parentDataFromDataTree, ...args),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
return enhancementFns;
|
|
}
|