diff --git a/app/client/src/actions/metaActions.ts b/app/client/src/actions/metaActions.ts index 3efe0d2ab5..6898e67266 100644 --- a/app/client/src/actions/metaActions.ts +++ b/app/client/src/actions/metaActions.ts @@ -29,12 +29,12 @@ export const updateWidgetMetaPropAndEval = ( export type ResetWidgetMetaPayload = { widgetId: string; - evaluatedWidget: DataTreeWidget; + evaluatedWidget: DataTreeWidget | undefined; }; export const resetWidgetMetaProperty = ( widgetId: string, - evaluatedWidget: DataTreeWidget, + evaluatedWidget: DataTreeWidget | undefined, ): BatchAction => { return batchAction({ type: ReduxActionTypes.RESET_WIDGET_META, diff --git a/app/client/src/reducers/entityReducers/metaReducer/index.ts b/app/client/src/reducers/entityReducers/metaReducer/index.ts index c521e11b67..03e3c68e5b 100644 --- a/app/client/src/reducers/entityReducers/metaReducer/index.ts +++ b/app/client/src/reducers/entityReducers/metaReducer/index.ts @@ -12,7 +12,7 @@ import { } from "@appsmith/constants/ReduxActionConstants"; import produce from "immer"; import { EvalMetaUpdates } from "workers/common/DataTreeEvaluator/types"; -import { klona } from "klona"; +import { getMetaWidgetResetObj } from "./metaReducerUtils"; export type WidgetMetaState = Record; export type MetaState = Record; @@ -104,25 +104,7 @@ export const metaReducer = createReducer(initialState, { if (widgetId in state) { // only reset widgets whose meta properties were changed. - // reset widget: sets the meta values to current default values of widget - const resetMetaObj: WidgetMetaState = {}; - - // evaluatedWidget is widget data inside dataTree, this will have latest default values of widget - if (evaluatedWidget) { - const { propertyOverrideDependency } = evaluatedWidget; - // propertyOverrideDependency has defaultProperty name for each meta property of widget - Object.entries(propertyOverrideDependency).map( - ([propertyName, dependency]) => { - const defaultPropertyValue = - dependency.DEFAULT && evaluatedWidget[dependency.DEFAULT]; - if (defaultPropertyValue !== undefined) { - // cloning data to avoid mutation - resetMetaObj[propertyName] = klona(defaultPropertyValue); - } - }, - ); - } - return { ...state, [widgetId]: resetMetaObj }; + state = { ...state, [widgetId]: getMetaWidgetResetObj(evaluatedWidget) }; } return state; }, diff --git a/app/client/src/reducers/entityReducers/metaReducer/metaReducerUtils.ts b/app/client/src/reducers/entityReducers/metaReducer/metaReducerUtils.ts new file mode 100644 index 0000000000..1d244e1e0b --- /dev/null +++ b/app/client/src/reducers/entityReducers/metaReducer/metaReducerUtils.ts @@ -0,0 +1,27 @@ +import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; +import { klona } from "klona"; +import { WidgetMetaState } from "."; + +export function getMetaWidgetResetObj( + evaluatedWidget: DataTreeWidget | undefined, +) { + // reset widget: sets the meta values to current default values of widget + const resetMetaObj: WidgetMetaState = {}; + + // evaluatedWidget is widget data inside dataTree, this will have latest default values of widget + if (evaluatedWidget) { + const { propertyOverrideDependency } = evaluatedWidget; + // propertyOverrideDependency has defaultProperty name for each meta property of widget + Object.entries(propertyOverrideDependency).map( + ([propertyName, dependency]) => { + const defaultPropertyValue = + dependency.DEFAULT && evaluatedWidget[dependency.DEFAULT]; + if (defaultPropertyValue !== undefined) { + // cloning data to avoid mutation + resetMetaObj[propertyName] = klona(defaultPropertyValue); + } + }, + ); + } + return resetMetaObj; +} diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 2811adc91d..6df4caf82c 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -10,7 +10,7 @@ import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; -import { getWidget, getWidgets } from "./selectors"; +import { getWidget, getWidgets, getWidgetsMeta } from "./selectors"; import { actionChannel, all, @@ -85,7 +85,7 @@ import { doesTriggerPathsContainPropertyPath, getParentBottomRowAfterAddingWidget, getParentWidgetIdForPasting, - getWidgetChildren, + getWidgetDescendantToReset, groupWidgetsIntoContainer, handleSpecificCasesWhilePasting, getSelectedWidgetWhenPasting, @@ -144,6 +144,7 @@ import { builderURL } from "RouteBuilder"; import history from "utils/history"; import { updateMultipleWidgetProperties } from "actions/widgetActions"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; +import { MetaState } from "reducers/entityReducers/metaReducer"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, @@ -814,10 +815,12 @@ function* resetChildrenMetaSaga(action: ReduxAction<{ widgetId: string }>) { const { widgetId: parentWidgetId } = action.payload; const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const evaluatedDataTree: DataTree = yield select(getDataTree); - const childrenList = getWidgetChildren( + const widgetsMeta: MetaState = yield select(getWidgetsMeta); + const childrenList = getWidgetDescendantToReset( canvasWidgets, parentWidgetId, evaluatedDataTree, + widgetsMeta, ); for (const childIndex in childrenList) { diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 91a21c4b0e..bc59421b49 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -4,7 +4,7 @@ import { getWidgetMetaProps, getWidgets, } from "./selectors"; -import _, { isString, remove } from "lodash"; +import _, { find, isString, reduce, remove } from "lodash"; import { CONTAINER_GRID_PADDING, GridDefaults, @@ -54,6 +54,7 @@ import { getBottomRowAfterReflow } from "utils/reflowHookUtils"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { isWidget } from "workers/Evaluation/evaluationUtils"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; +import { MetaState } from "reducers/entityReducers/metaReducer"; export interface CopiedWidgetGroup { widgetId: string; @@ -301,53 +302,114 @@ export function getWidgetChildrenIds( } return childrenIds; } +function sortWidgetsMetaByParent(widgetsMeta: MetaState, parentId: string) { + return reduce( + widgetsMeta, + function( + result: { + childrenWidgetsMeta: MetaState; + otherWidgetsMeta: MetaState; + }, + currentWidgetMeta, + key, + ) { + return key.startsWith(parentId + "_") + ? { + ...result, + childrenWidgetsMeta: { + ...result.childrenWidgetsMeta, + [key]: currentWidgetMeta, + }, + } + : { + ...result, + otherWidgetsMeta: { + ...result.otherWidgetsMeta, + [key]: currentWidgetMeta, + }, + }; + }, + { + childrenWidgetsMeta: {}, + otherWidgetsMeta: {}, + }, + ); +} + +export type DescendantWidgetMap = { + id: string; + // To accomodate metaWidgets which might not be present on the evalTree, evaluatedWidget might be undefined + evaluatedWidget: DataTreeWidget | undefined; +}; -export type ChildrenWidgetMap = { id: string; evaluatedWidget: DataTreeWidget }; /** - * getWidgetChildren: It gets all the child widgets of given widget's id with evaluated values - * + * As part of widget's descendant, we add both children and metaWidgets. + * children are assessed from "widget.children" + * metaWidgets are assessed from the metaState, since we care about only metawidgets whose values have been changed. + * NB: metaWidgets id start with parentId + "_" */ -export function getWidgetChildren( +export function getWidgetDescendantToReset( canvasWidgets: CanvasWidgetsReduxState, widgetId: string, evaluatedDataTree: DataTree, -): ChildrenWidgetMap[] { - const childrenList: ChildrenWidgetMap[] = []; + widgetsMeta: MetaState, +): DescendantWidgetMap[] { + const descendantList: DescendantWidgetMap[] = []; const widget = _.get(canvasWidgets, widgetId); - // When a form widget tries to resetChildrenMetaProperties - // But one or more of its container like children - // have just been deleted, widget can be undefined - if (widget === undefined) { - return []; + + const sortedWidgetsMeta = sortWidgetsMetaByParent(widgetsMeta, widgetId); + for (const childMetaWidgetId of Object.keys( + sortedWidgetsMeta.childrenWidgetsMeta, + )) { + const evaluatedChildWidget = find(evaluatedDataTree, function(entity) { + return isWidget(entity) && entity.widgetId === childMetaWidgetId; + }) as DataTreeWidget | undefined; + descendantList.push({ + id: childMetaWidgetId, + evaluatedWidget: evaluatedChildWidget, + }); + const grandChildren = getWidgetDescendantToReset( + canvasWidgets, + childMetaWidgetId, + evaluatedDataTree, + sortedWidgetsMeta.otherWidgetsMeta, + ); + if (grandChildren.length) { + descendantList.push(...grandChildren); + } } - const { children = [] } = widget; - if (children && children.length) { - for (const childIndex in children) { - if (children.hasOwnProperty(childIndex)) { - const childWidgetId = children[childIndex]; + if (widget) { + const { children = [] } = widget; + if (children && children.length) { + for (const childIndex in children) { + if (children.hasOwnProperty(childIndex)) { + const childWidgetId = children[childIndex]; - const childCanvasWidget = _.get(canvasWidgets, childWidgetId); - const childWidgetName = childCanvasWidget.widgetName; - const childWidget = evaluatedDataTree[childWidgetName]; - if (isWidget(childWidget)) { - childrenList.push({ - id: childWidgetId, - evaluatedWidget: childWidget, - }); - const grandChildren = getWidgetChildren( - canvasWidgets, - childWidgetId, - evaluatedDataTree, - ); - if (grandChildren.length) { - childrenList.push(...grandChildren); + const childCanvasWidget = _.get(canvasWidgets, childWidgetId); + const childWidgetName = childCanvasWidget.widgetName; + const childWidget = evaluatedDataTree[childWidgetName]; + if (isWidget(childWidget)) { + descendantList.push({ + id: childWidgetId, + evaluatedWidget: childWidget, + }); + const grandChildren = getWidgetDescendantToReset( + canvasWidgets, + childWidgetId, + evaluatedDataTree, + sortedWidgetsMeta.otherWidgetsMeta, + ); + if (grandChildren.length) { + descendantList.push(...grandChildren); + } } } } } } - return childrenList; + + return descendantList; } export const getParentWidgetIdForPasting = function*(