fix: List Widget shows cyclic dependency error when children widgets with bindings in action are deleted (#8942)
* fix list widget dynamic trigger path issue * Update app/client/src/sagas/WidgetOperationUtils.ts Co-authored-by: Hetu Nandu <hetunandu@gmail.com> Co-authored-by: root <root@DESKTOP-9GENCK0.localdomain> Co-authored-by: Hetu Nandu <hetunandu@gmail.com>
This commit is contained in:
parent
229412e19d
commit
4005f23baa
|
|
@ -14,7 +14,7 @@ import {
|
|||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
import { ENTITY_TYPE } from "entities/AppsmithConsole";
|
||||
import LOG_TYPE from "entities/AppsmithConsole/logtype";
|
||||
import { flattenDeep, omit, remove } from "lodash";
|
||||
import { flattenDeep, omit } from "lodash";
|
||||
import {
|
||||
CanvasWidgetsReduxState,
|
||||
FlattenedWidgetProps,
|
||||
|
|
@ -26,8 +26,10 @@ import AppsmithConsole from "utils/AppsmithConsole";
|
|||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { clearEvalPropertyCacheOfWidget } from "./EvaluationsSaga";
|
||||
import { getSelectedWidget, getWidget, getWidgets } from "./selectors";
|
||||
import { getParentWithEnhancementFn } from "./WidgetEnhancementHelpers";
|
||||
import { getAllWidgetsInTree } from "./WidgetOperationUtils";
|
||||
import {
|
||||
getAllWidgetsInTree,
|
||||
updateListWidgetPropertiesOnChildDelete,
|
||||
} from "./WidgetOperationUtils";
|
||||
import { showUndoRedoToast } from "utils/replayHelpers";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
const WidgetTypes = WidgetFactory.widgetTypes;
|
||||
|
|
@ -40,42 +42,6 @@ type WidgetDeleteTabChild = {
|
|||
widgetId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* this saga clears out the enhancementMap, template and dynamicBindingPathList when a child
|
||||
* is deleted in list widget
|
||||
*
|
||||
* @param widgets
|
||||
* @param widgetId
|
||||
* @param widgetName
|
||||
* @param parentId
|
||||
*/
|
||||
function* updateListWidgetPropertiesOnChildDelete(
|
||||
widgets: CanvasWidgetsReduxState,
|
||||
widgetId: string,
|
||||
widgetName: string,
|
||||
) {
|
||||
const clone = JSON.parse(JSON.stringify(widgets));
|
||||
|
||||
const parentWithEnhancementFn = getParentWithEnhancementFn(widgetId, clone);
|
||||
|
||||
if (parentWithEnhancementFn?.type === "LIST_WIDGET") {
|
||||
const listWidget = parentWithEnhancementFn;
|
||||
|
||||
// delete widget in template of list
|
||||
if (listWidget && widgetName in listWidget.template) {
|
||||
listWidget.template[widgetName] = undefined;
|
||||
}
|
||||
|
||||
// delete dynamic binding path if any
|
||||
remove(listWidget?.dynamicBindingPathList || [], (path: any) =>
|
||||
path.key.startsWith(`template.${widgetName}`),
|
||||
);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
function* deleteTabChildSaga(
|
||||
deleteChildTabAction: ReduxAction<WidgetDeleteTabChild>,
|
||||
) {
|
||||
|
|
@ -177,8 +143,7 @@ function* getUpdatedDslAfterDeletingWidget(widgetId: string, parentId: string) {
|
|||
|
||||
yield call(clearEvalPropertyCacheOfWidget, widgetName);
|
||||
|
||||
let finalWidgets: CanvasWidgetsReduxState = yield call(
|
||||
updateListWidgetPropertiesOnChildDelete,
|
||||
let finalWidgets: CanvasWidgetsReduxState = updateListWidgetPropertiesOnChildDelete(
|
||||
widgets,
|
||||
widgetId,
|
||||
widgetName,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
handleSpecificCasesWhilePasting,
|
||||
doesTriggerPathsContainPropertyPath,
|
||||
checkIfPastingIntoListWidget,
|
||||
updateListWidgetPropertiesOnChildDelete,
|
||||
} from "./WidgetOperationUtils";
|
||||
|
||||
describe("WidgetOperationSaga", () => {
|
||||
|
|
@ -456,4 +457,128 @@ describe("WidgetOperationSaga", () => {
|
|||
|
||||
expect(result?.type).toStrictEqual("LIST_WIDGET");
|
||||
});
|
||||
|
||||
it("should return widgets after executing updateListWidgetPropertiesOnChildDelete", () => {
|
||||
const result = updateListWidgetPropertiesOnChildDelete(
|
||||
{
|
||||
list1: {
|
||||
widgetId: "list1",
|
||||
type: "LIST_WIDGET",
|
||||
widgetName: "List1",
|
||||
parentId: "0",
|
||||
renderMode: "CANVAS",
|
||||
parentColumnSpace: 2,
|
||||
parentRowSpace: 3,
|
||||
leftColumn: 2,
|
||||
rightColumn: 3,
|
||||
topRow: 1,
|
||||
bottomRow: 3,
|
||||
isLoading: false,
|
||||
listData: [],
|
||||
version: 16,
|
||||
disablePropertyPane: false,
|
||||
template: {},
|
||||
enhancements: {},
|
||||
dynamicBindingPathList: [{ key: "template.ButtonWidget1.text" }],
|
||||
dynamicTriggerPathList: [
|
||||
{
|
||||
key: "template.ButtonWidget1.onClick",
|
||||
},
|
||||
],
|
||||
},
|
||||
buttonWidget1: {
|
||||
type: "BUTTON_WIDGET",
|
||||
widgetId: "buttonWidget1",
|
||||
widgetName: "buttonWidget1",
|
||||
version: 16,
|
||||
parentColumnSpace: 2,
|
||||
parentRowSpace: 3,
|
||||
leftColumn: 2,
|
||||
rightColumn: 3,
|
||||
topRow: 1,
|
||||
bottomRow: 3,
|
||||
renderMode: "CANVAS",
|
||||
isLoading: false,
|
||||
parentId: "list1",
|
||||
},
|
||||
0: {
|
||||
type: "CANVAS_WIDGET",
|
||||
widgetId: "0",
|
||||
widgetName: "MainContainer",
|
||||
version: 16,
|
||||
parentColumnSpace: 2,
|
||||
parentRowSpace: 3,
|
||||
leftColumn: 2,
|
||||
rightColumn: 3,
|
||||
topRow: 1,
|
||||
bottomRow: 3,
|
||||
renderMode: "CANVAS",
|
||||
isLoading: false,
|
||||
parentId: "list1",
|
||||
},
|
||||
},
|
||||
"buttonWidget1",
|
||||
"ButtonWidget1",
|
||||
);
|
||||
|
||||
const expected = updateListWidgetPropertiesOnChildDelete(
|
||||
{
|
||||
list1: {
|
||||
widgetId: "list1",
|
||||
type: "LIST_WIDGET",
|
||||
widgetName: "List1",
|
||||
parentId: "0",
|
||||
renderMode: "CANVAS",
|
||||
parentColumnSpace: 2,
|
||||
parentRowSpace: 3,
|
||||
leftColumn: 2,
|
||||
rightColumn: 3,
|
||||
topRow: 1,
|
||||
bottomRow: 3,
|
||||
isLoading: false,
|
||||
listData: [],
|
||||
version: 16,
|
||||
disablePropertyPane: false,
|
||||
template: {},
|
||||
enhancements: {},
|
||||
dynamicBindingPathList: [],
|
||||
dynamicTriggerPathList: [],
|
||||
},
|
||||
buttonWidget1: {
|
||||
type: "BUTTON_WIDGET",
|
||||
widgetId: "buttonWidget1",
|
||||
widgetName: "buttonWidget1",
|
||||
version: 16,
|
||||
parentColumnSpace: 2,
|
||||
parentRowSpace: 3,
|
||||
leftColumn: 2,
|
||||
rightColumn: 3,
|
||||
topRow: 1,
|
||||
bottomRow: 3,
|
||||
renderMode: "CANVAS",
|
||||
isLoading: false,
|
||||
parentId: "list1",
|
||||
},
|
||||
0: {
|
||||
type: "CANVAS_WIDGET",
|
||||
widgetId: "0",
|
||||
widgetName: "MainContainer",
|
||||
version: 16,
|
||||
parentColumnSpace: 2,
|
||||
parentRowSpace: 3,
|
||||
leftColumn: 2,
|
||||
rightColumn: 3,
|
||||
topRow: 1,
|
||||
bottomRow: 3,
|
||||
renderMode: "CANVAS",
|
||||
isLoading: false,
|
||||
parentId: "list1",
|
||||
},
|
||||
},
|
||||
"buttonWidget1",
|
||||
"ButtonWidget1",
|
||||
);
|
||||
|
||||
expect(result).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
getWidgetMetaProps,
|
||||
getWidgets,
|
||||
} from "./selectors";
|
||||
import _, { isString } from "lodash";
|
||||
import _, { isString, remove } from "lodash";
|
||||
import {
|
||||
GridDefaults,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
|
|
@ -29,6 +29,7 @@ import {
|
|||
} from "utils/DynamicBindingUtils";
|
||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { getParentWithEnhancementFn } from "./WidgetEnhancementHelpers";
|
||||
|
||||
export interface CopiedWidgetGroup {
|
||||
widgetId: string;
|
||||
|
|
@ -817,3 +818,45 @@ export function* getParentWidgetIdForGrouping(
|
|||
|
||||
return pastingIntoWidgetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* this saga clears out the enhancementMap, template, dynamicBindingPathList and dynamicTriggerPathList when a child
|
||||
* is deleted in list widget
|
||||
*
|
||||
* @param widgets
|
||||
* @param widgetId
|
||||
* @param widgetName
|
||||
* @param parentId
|
||||
*/
|
||||
export function updateListWidgetPropertiesOnChildDelete(
|
||||
widgets: CanvasWidgetsReduxState,
|
||||
widgetId: string,
|
||||
widgetName: string,
|
||||
) {
|
||||
const clone = JSON.parse(JSON.stringify(widgets));
|
||||
|
||||
const parentWithEnhancementFn = getParentWithEnhancementFn(widgetId, clone);
|
||||
|
||||
if (parentWithEnhancementFn?.type === "LIST_WIDGET") {
|
||||
const listWidget = parentWithEnhancementFn;
|
||||
|
||||
// delete widget in template of list
|
||||
if (listWidget && widgetName in listWidget.template) {
|
||||
listWidget.template[widgetName] = undefined;
|
||||
}
|
||||
|
||||
// delete dynamic binding path if any
|
||||
remove(listWidget?.dynamicBindingPathList || [], (path: any) =>
|
||||
path.key.startsWith(`template.${widgetName}`),
|
||||
);
|
||||
|
||||
// delete dynamic trigger path if any
|
||||
remove(listWidget?.dynamicTriggerPathList || [], (path: any) =>
|
||||
path.key.startsWith(`template.${widgetName}`),
|
||||
);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user