Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: currentItem showing error in debugger for list widgets #10338

Merged
2 changes: 1 addition & 1 deletion app/client/src/constants/WidgetConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const layoutConfigurations: LayoutConfigurations = {
FLUID: { minWidth: -1, maxWidth: -1 },
};

export const LATEST_PAGE_VERSION = 49;
export const LATEST_PAGE_VERSION = 50;

export const GridDefaults = {
DEFAULT_CELL_SIZE: 1,
Expand Down
6 changes: 6 additions & 0 deletions app/client/src/entities/DataTree/dataTreeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export enum EvaluationSubstitutionType {
SMART_SUBSTITUTE = "SMART_SUBSTITUTE",
}

// Private widgets do not get evaluated
// For example, for widget Button1 in a List widget List1, List1.template.Button1.text gets evaluated,
// so there is no need to evaluate Button1.text
export type PrivateWidgets = Record<string, true>;

export interface DataTreeAction
extends Omit<ActionDataWithMeta, "data" | "config"> {
data: ActionResponse["body"];
Expand Down Expand Up @@ -81,6 +86,7 @@ export interface DataTreeWidget extends WidgetProps {
validationPaths: Record<string, ValidationConfig>;
ENTITY_TYPE: ENTITY_TYPE.WIDGET;
logBlackList: Record<string, true>;
privateWidgets: PrivateWidgets;
}

export interface DataTreeAppsmith extends Omit<AppDataState, "store"> {
Expand Down
1 change: 1 addition & 0 deletions app/client/src/entities/DataTree/dataTreeWidget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ describe("generateDataTreeWidget", () => {
defaultProps: {
text: "defaultText",
},
privateWidgets: {},
};

const result = generateDataTreeWidget(widget, widgetMetaProps);
Expand Down
3 changes: 3 additions & 0 deletions app/client/src/entities/DataTree/dataTreeWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,8 @@ export const generateDataTreeWidget = (
triggerPaths,
validationPaths,
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
privateWidgets: {
...widget.privateWidgets,
},
};
};
1 change: 1 addition & 0 deletions app/client/src/selectors/editorSelectors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ const createLoadingWidget = (
validationPaths: {},
logBlackList: {},
isLoading: true,
privateWidgets: {},
};
};

Expand Down
32 changes: 32 additions & 0 deletions app/client/src/utils/DSLMigrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { migrateCheckboxGroupWidgetInlineProperty } from "./migrations/CheckboxG
import { migrateMapWidgetIsClickedMarkerCentered } from "./migrations/MapWidget";
import { DSLWidget } from "widgets/constants";
import { migrateRecaptchaType } from "./migrations/ButtonWidgetMigrations";
import { PrivateWidgets } from "entities/DataTree/dataTreeFactory";

/**
* adds logBlackList key for all list widget children
Expand Down Expand Up @@ -83,6 +84,32 @@ const addLogBlackListToAllListWidgetChildren = (
return currentDSL;
};

/**
* adds 'privateWidgets' key for all list widgets
*
* @param currentDSL
* @returns
*/
const addPrivateWidgetsToAllListWidgets = (
currentDSL: ContainerWidgetProps<WidgetProps>,
) => {
currentDSL.children = currentDSL.children?.map((child: WidgetProps) => {
if (child.type === "LIST_WIDGET") {
const privateWidgets: PrivateWidgets = {};
Object.keys(child.template).forEach((entityName) => {
privateWidgets[entityName] = true;
});

if (!child.privateWidgets) {
set(child, `privateWidgets`, privateWidgets);
}
}
return child;
});

return currentDSL;
};

/**
* changes items -> listData
*
Expand Down Expand Up @@ -1001,6 +1028,11 @@ export const transformDSL = (

if (currentDSL.version === 48) {
currentDSL = migrateRecaptchaType(currentDSL);
currentDSL.version = 49;
}

if (currentDSL.version === 49) {
currentDSL = addPrivateWidgetsToAllListWidgets(currentDSL);
currentDSL.version = LATEST_PAGE_VERSION;
}

Expand Down
1,170 changes: 1,170 additions & 0 deletions app/client/src/utils/DSLMigrationsUtils.test.ts

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion app/client/src/widgets/ListWidget/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ export const CONFIG = {
...get(parent, "template", {}),
[widget.widgetName]: widget,
};

parent.template = template;

// add logBlackList for the children being added
Expand Down
40 changes: 40 additions & 0 deletions app/client/src/widgets/ListWidget/widget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
omit,
floor,
isEmpty,
isEqual,
} from "lodash";
import memoizeOne from "memoize-one";
import shallowEqual from "shallowequal";
Expand All @@ -37,8 +38,15 @@ import derivedProperties from "./parseDerivedProperties";
import { DSLWidget } from "widgets/constants";
import { entityDefinitions } from "utils/autocomplete/EntityDefinitions";
import { escapeSpecialChars } from "../../WidgetUtils";
import { PrivateWidgets } from "entities/DataTree/dataTreeFactory";

const LIST_WIDGEY_PAGINATION_HEIGHT = 36;

/* in the List Widget, "children.0.children.0.children.0.children" is the path to the list of all
widgets present in the List Widget
*/
const PATH_TO_ALL_WIDGETS_IN_LIST_WIDGET =
"children.0.children.0.children.0.children";
class ListWidget extends BaseWidget<ListWidgetProps<WidgetProps>, WidgetState> {
state = {
page: 1,
Expand Down Expand Up @@ -73,6 +81,9 @@ class ListWidget extends BaseWidget<ListWidgetProps<WidgetProps>, WidgetState> {
this.generateChildrenDefaultPropertiesMap(this.props);
this.generateChildrenMetaPropertiesMap(this.props);
this.generateChildrenEntityDefinitions(this.props);

// add privateWidgets to ListWidget
this.addPrivateWidgetsForChildren(this.props);
}

/**
Expand Down Expand Up @@ -113,6 +124,20 @@ class ListWidget extends BaseWidget<ListWidgetProps<WidgetProps>, WidgetState> {
}
}

// updates the "privateWidgets" field of the List Widget
addPrivateWidgetsForChildren(props: ListWidgetProps<WidgetProps>) {
const privateWidgets: PrivateWidgets = {};
const listWidgetChildren: WidgetProps[] = get(
props,
PATH_TO_ALL_WIDGETS_IN_LIST_WIDGET,
);
listWidgetChildren.map((child) => {
privateWidgets[child.widgetName] = true;
});

super.updateWidgetProperty("privateWidgets", privateWidgets);
}

generateChildrenDefaultPropertiesMap = (
props: ListWidgetProps<WidgetProps>,
) => {
Expand Down Expand Up @@ -174,6 +199,16 @@ class ListWidget extends BaseWidget<ListWidgetProps<WidgetProps>, WidgetState> {
};

componentDidUpdate(prevProps: ListWidgetProps<WidgetProps>) {
const currentListWidgetChildren: WidgetProps[] = get(
this.props,
PATH_TO_ALL_WIDGETS_IN_LIST_WIDGET,
);

const previousListWidgetChildren: WidgetProps[] = get(
prevProps,
PATH_TO_ALL_WIDGETS_IN_LIST_WIDGET,
);

if (
xor(
Object.keys(get(prevProps, "template", {})),
Expand Down Expand Up @@ -233,6 +268,11 @@ class ListWidget extends BaseWidget<ListWidgetProps<WidgetProps>, WidgetState> {
},
);
}

// Update privateWidget field if there is a change in the List widget children
if (!isEqual(currentListWidgetChildren, previousListWidgetChildren)) {
this.addPrivateWidgetsForChildren(this.props);
}
}

static getDefaultPropertiesMap(): Record<string, string> {
Expand Down
18 changes: 18 additions & 0 deletions app/client/src/workers/DataTreeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
DataTreeWidget,
ENTITY_TYPE,
EvaluationSubstitutionType,
PrivateWidgets,
} from "entities/DataTree/dataTreeFactory";
import {
addDependantsOfNestedPropertyPaths,
Expand All @@ -46,6 +47,7 @@ import {
getParams,
updateJSCollectionInDataTree,
removeFunctionsAndVariableJSCollection,
isPrivateEntityPath,
} from "workers/evaluationUtils";
import _ from "lodash";
import { applyChange, Diff, diff } from "deep-diff";
Expand Down Expand Up @@ -78,6 +80,7 @@ export default class DataTreeEvaluator {
widgetConfigMap: WidgetTypeConfigMap = {};
evalTree: DataTree = {};
allKeys: Record<string, true> = {};
privateWidgets: PrivateWidgets = {};
oldUnEvalTree: DataTree = {};
errors: EvalError[] = [];
resolvedFunctions: Record<string, any> = {};
Expand Down Expand Up @@ -471,6 +474,17 @@ export default class DataTreeEvaluator {
return dependencyMap;
}

getPrivateWidgets(dataTree: DataTree): PrivateWidgets {
let privateWidgets: PrivateWidgets = {};
Object.keys(dataTree).forEach((entityName) => {
const entity = dataTree[entityName];
if (isWidget(entity) && !_.isEmpty(entity.privateWidgets)) {
privateWidgets = { ...privateWidgets, ...entity.privateWidgets };
}
});
return privateWidgets;
}

listEntityDependencies(
entity: DataTreeWidget | DataTreeAction | DataTreeJSAction,
entityName: string,
Expand Down Expand Up @@ -556,9 +570,13 @@ export default class DataTreeEvaluator {
sortedDependencies: Array<string>,
): DataTree {
const tree = _.cloneDeep(oldUnevalTree);
this.privateWidgets = this.getPrivateWidgets(oldUnevalTree);
try {
return sortedDependencies.reduce(
(currentTree: DataTree, fullPropertyPath: string) => {
// do not evaluate private entities
if (isPrivateEntityPath(this.privateWidgets, fullPropertyPath))
return currentTree;
const { entityName, propertyPath } = getEntityNameAndPropertyPath(
fullPropertyPath,
);
Expand Down
23 changes: 21 additions & 2 deletions app/client/src/workers/evaluationUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getAllPaths } from "./evaluationUtils";
import { PrivateWidgets } from "entities/DataTree/dataTreeFactory";
import { getAllPaths, isPrivateEntityPath } from "./evaluationUtils";

describe("getAllPaths", () => {
describe("Correctly handle paths", () => {
it("getsAllPaths", () => {
const myTree = {
WidgetName: {
Expand Down Expand Up @@ -38,4 +39,22 @@ describe("getAllPaths", () => {
const actual = getAllPaths(myTree);
expect(actual).toStrictEqual(result);
});

it("correctly checks if path is a PrivateEntityPath", () => {
const privateWidgets: PrivateWidgets = {
Button1: true,
Image1: true,
Button2: true,
Image2: true,
};

expect(
isPrivateEntityPath(privateWidgets, "List1.template.Button1.text"),
).toBeFalsy();
expect(isPrivateEntityPath(privateWidgets, "Button1.text")).toBeTruthy();
expect(
isPrivateEntityPath(privateWidgets, "List2.template.Image2.data"),
).toBeFalsy();
expect(isPrivateEntityPath(privateWidgets, "Image2.data")).toBeTruthy();
});
});
12 changes: 12 additions & 0 deletions app/client/src/workers/evaluationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ENTITY_TYPE,
DataTreeJSAction,
EvaluationSubstitutionType,
PrivateWidgets,
} from "entities/DataTree/dataTreeFactory";
import _ from "lodash";
import { WidgetTypeConfigMap } from "utils/WidgetFactory";
Expand Down Expand Up @@ -710,3 +711,14 @@ export const removeFunctionsAndVariableJSCollection = (
_.set(modifiedDataTree, `${entity.name}.meta`, meta);
return modifiedDataTree;
};

export const isPrivateEntityPath = (
privateWidgets: PrivateWidgets,
fullPropertyPath: string,
) => {
const entityName = fullPropertyPath.split(".")[0];
if (Object.keys(privateWidgets).indexOf(entityName) !== -1) {
return true;
}
return false;
};