fix: table search text dependency deletion (#11608)
* Replace BindingPaths with ReactivePaths and remove non-bindable property path from bindingPaths to reduce confusion.
This commit is contained in:
parent
68bca33a55
commit
136308fd7c
|
|
@ -1,5 +1,5 @@
|
||||||
import { Action, PluginType } from "entities/Action/index";
|
import { Action, PluginType } from "entities/Action/index";
|
||||||
import { getBindingPathsOfAction } from "entities/Action/actionProperties";
|
import { getBindingAndReactivePathsOfAction } from "entities/Action/actionProperties";
|
||||||
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
||||||
|
|
||||||
const DEFAULT_ACTION: Action = {
|
const DEFAULT_ACTION: Action = {
|
||||||
|
|
@ -18,12 +18,16 @@ const DEFAULT_ACTION: Action = {
|
||||||
organizationId: "",
|
organizationId: "",
|
||||||
pageId: "",
|
pageId: "",
|
||||||
pluginId: "",
|
pluginId: "",
|
||||||
|
messages: [],
|
||||||
pluginType: PluginType.DB,
|
pluginType: PluginType.DB,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("getBindingPathsOfAction", () => {
|
describe("getReactivePathsOfAction", () => {
|
||||||
it("returns default list of no config is sent", () => {
|
it("returns default list of no config is sent", () => {
|
||||||
const response = getBindingPathsOfAction(DEFAULT_ACTION, undefined);
|
const response = getBindingAndReactivePathsOfAction(
|
||||||
|
DEFAULT_ACTION,
|
||||||
|
undefined,
|
||||||
|
).reactivePaths;
|
||||||
expect(response).toStrictEqual({
|
expect(response).toStrictEqual({
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -73,7 +77,8 @@ describe("getBindingPathsOfAction", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = getBindingPathsOfAction(basicAction, config);
|
const response = getBindingAndReactivePathsOfAction(basicAction, config)
|
||||||
|
.reactivePaths;
|
||||||
expect(response).toStrictEqual({
|
expect(response).toStrictEqual({
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -139,7 +144,8 @@ describe("getBindingPathsOfAction", () => {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const response = getBindingPathsOfAction(basicAction, config);
|
const response = getBindingAndReactivePathsOfAction(basicAction, config)
|
||||||
|
.reactivePaths;
|
||||||
expect(response).toStrictEqual({
|
expect(response).toStrictEqual({
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -195,7 +201,8 @@ describe("getBindingPathsOfAction", () => {
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const response = getBindingPathsOfAction(basicAction, config);
|
const response = getBindingAndReactivePathsOfAction(basicAction, config)
|
||||||
|
.reactivePaths;
|
||||||
expect(response).toStrictEqual({
|
expect(response).toStrictEqual({
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -205,7 +212,7 @@ describe("getBindingPathsOfAction", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("checks for hidden field and returns bindingPaths accordingly", () => {
|
it("checks for hidden field and returns reactivePaths accordingly", () => {
|
||||||
const config = [
|
const config = [
|
||||||
{
|
{
|
||||||
sectionName: "",
|
sectionName: "",
|
||||||
|
|
@ -252,7 +259,8 @@ describe("getBindingPathsOfAction", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = getBindingPathsOfAction(basicAction, config);
|
const response = getBindingAndReactivePathsOfAction(basicAction, config)
|
||||||
|
.reactivePaths;
|
||||||
expect(response).toStrictEqual({
|
expect(response).toStrictEqual({
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -263,7 +271,8 @@ describe("getBindingPathsOfAction", () => {
|
||||||
|
|
||||||
basicAction.actionConfiguration.template.setting = true;
|
basicAction.actionConfiguration.template.setting = true;
|
||||||
|
|
||||||
const response2 = getBindingPathsOfAction(basicAction, config);
|
const response2 = getBindingAndReactivePathsOfAction(basicAction, config)
|
||||||
|
.reactivePaths;
|
||||||
expect(response2).toStrictEqual({
|
expect(response2).toStrictEqual({
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -272,4 +281,63 @@ describe("getBindingPathsOfAction", () => {
|
||||||
"config.body2": EvaluationSubstitutionType.TEMPLATE,
|
"config.body2": EvaluationSubstitutionType.TEMPLATE,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.only("returns default list of no config is sent", () => {
|
||||||
|
const response = getBindingAndReactivePathsOfAction(
|
||||||
|
DEFAULT_ACTION,
|
||||||
|
undefined,
|
||||||
|
).bindingPaths;
|
||||||
|
expect(response).toStrictEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.only("returns correct values for basic config", () => {
|
||||||
|
const config = [
|
||||||
|
{
|
||||||
|
sectionName: "",
|
||||||
|
id: 1,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
configProperty: "actionConfiguration.body",
|
||||||
|
controlType: "QUERY_DYNAMIC_TEXT",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
configProperty: "actionConfiguration.body2",
|
||||||
|
controlType: "QUERY_DYNAMIC_INPUT_TEXT",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
configProperty: "actionConfiguration.field1",
|
||||||
|
controlType: "QUERY_DYNAMIC_INPUT_TEXT",
|
||||||
|
evaluationSubstitutionType: "SMART_SUBSTITUTE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
configProperty: "actionConfiguration.field2",
|
||||||
|
controlType: "QUERY_DYNAMIC_INPUT_TEXT",
|
||||||
|
evaluationSubstitutionType: "PARAMETER",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const basicAction = {
|
||||||
|
...DEFAULT_ACTION,
|
||||||
|
actionConfiguration: {
|
||||||
|
body: "basic action",
|
||||||
|
body2: "another body",
|
||||||
|
field1: "test",
|
||||||
|
field2: "anotherTest",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = getBindingAndReactivePathsOfAction(basicAction, config)
|
||||||
|
.bindingPaths;
|
||||||
|
expect(response).toStrictEqual({
|
||||||
|
"config.body": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"config.body2": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"config.field1": EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
||||||
|
"config.field2": EvaluationSubstitutionType.PARAMETER,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,15 @@ import {
|
||||||
WhereClauseSubComponent,
|
WhereClauseSubComponent,
|
||||||
allowedControlTypes,
|
allowedControlTypes,
|
||||||
} from "components/formControls/utils";
|
} from "components/formControls/utils";
|
||||||
|
import formControlTypes from "utils/formControl/formControlTypes";
|
||||||
|
|
||||||
const dynamicFields = ["QUERY_DYNAMIC_TEXT", "QUERY_DYNAMIC_INPUT_TEXT"];
|
const dynamicFields = [
|
||||||
|
formControlTypes.QUERY_DYNAMIC_TEXT,
|
||||||
|
formControlTypes.QUERY_DYNAMIC_INPUT_TEXT,
|
||||||
|
];
|
||||||
|
|
||||||
|
type ReactivePaths = Record<string, EvaluationSubstitutionType>;
|
||||||
|
type BindingPaths = ReactivePaths;
|
||||||
const getCorrectEvaluationSubstitutionType = (substitutionType?: string) => {
|
const getCorrectEvaluationSubstitutionType = (substitutionType?: string) => {
|
||||||
if (substitutionType) {
|
if (substitutionType) {
|
||||||
if (substitutionType === EvaluationSubstitutionType.SMART_SUBSTITUTE) {
|
if (substitutionType === EvaluationSubstitutionType.SMART_SUBSTITUTE) {
|
||||||
|
|
@ -26,20 +32,25 @@ const getCorrectEvaluationSubstitutionType = (substitutionType?: string) => {
|
||||||
return EvaluationSubstitutionType.TEMPLATE;
|
return EvaluationSubstitutionType.TEMPLATE;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBindingPathsOfAction = (
|
export const getBindingAndReactivePathsOfAction = (
|
||||||
action: Action,
|
action: Action,
|
||||||
formConfig?: any[],
|
formConfig?: any[],
|
||||||
): Record<string, EvaluationSubstitutionType> => {
|
): { reactivePaths: ReactivePaths; bindingPaths: BindingPaths } => {
|
||||||
const bindingPaths: Record<string, EvaluationSubstitutionType> = {
|
let reactivePaths: ReactivePaths = {
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
datasourceUrl: EvaluationSubstitutionType.TEMPLATE,
|
datasourceUrl: EvaluationSubstitutionType.TEMPLATE,
|
||||||
};
|
};
|
||||||
|
const bindingPaths: BindingPaths = {};
|
||||||
if (!formConfig) {
|
if (!formConfig) {
|
||||||
return {
|
reactivePaths = {
|
||||||
...bindingPaths,
|
...reactivePaths,
|
||||||
config: EvaluationSubstitutionType.TEMPLATE,
|
config: EvaluationSubstitutionType.TEMPLATE,
|
||||||
};
|
};
|
||||||
|
return {
|
||||||
|
reactivePaths,
|
||||||
|
bindingPaths,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const recursiveFindBindingPaths = (formConfig: any) => {
|
const recursiveFindBindingPaths = (formConfig: any) => {
|
||||||
if (formConfig.children) {
|
if (formConfig.children) {
|
||||||
|
|
@ -61,7 +72,7 @@ export const getBindingPathsOfAction = (
|
||||||
bindingPaths[configPath] = getCorrectEvaluationSubstitutionType(
|
bindingPaths[configPath] = getCorrectEvaluationSubstitutionType(
|
||||||
alternateViewTypeInputConfig.evaluationSubstitutionType,
|
alternateViewTypeInputConfig.evaluationSubstitutionType,
|
||||||
);
|
);
|
||||||
} else if (formConfig.controlType === "ARRAY_FIELD") {
|
} else if (formConfig.controlType === formControlTypes.ARRAY_FIELD) {
|
||||||
let actionValue = _.get(action, formConfig.configProperty);
|
let actionValue = _.get(action, formConfig.configProperty);
|
||||||
if (Array.isArray(actionValue)) {
|
if (Array.isArray(actionValue)) {
|
||||||
actionValue = actionValue.filter((val) => val);
|
actionValue = actionValue.filter((val) => val);
|
||||||
|
|
@ -81,7 +92,7 @@ export const getBindingPathsOfAction = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (formConfig.controlType === "WHERE_CLAUSE") {
|
} else if (formConfig.controlType === formControlTypes.WHERE_CLAUSE) {
|
||||||
const recursiveFindBindingPathsForWhereClause = (
|
const recursiveFindBindingPathsForWhereClause = (
|
||||||
newConfigPath: string,
|
newConfigPath: string,
|
||||||
actionValue: any,
|
actionValue: any,
|
||||||
|
|
@ -138,7 +149,7 @@ export const getBindingPathsOfAction = (
|
||||||
recursiveFindBindingPathsForWhereClause(childrenPath, value);
|
recursiveFindBindingPathsForWhereClause(childrenPath, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (formConfig.controlType === "PAGINATION") {
|
} else if (formConfig.controlType === formControlTypes.PAGINATION) {
|
||||||
const limitPath = getBindingOrConfigPathsForPaginationControl(
|
const limitPath = getBindingOrConfigPathsForPaginationControl(
|
||||||
PaginationSubComponent.Offset,
|
PaginationSubComponent.Offset,
|
||||||
configPath,
|
configPath,
|
||||||
|
|
@ -153,7 +164,7 @@ export const getBindingPathsOfAction = (
|
||||||
bindingPaths[offsetPath] = getCorrectEvaluationSubstitutionType(
|
bindingPaths[offsetPath] = getCorrectEvaluationSubstitutionType(
|
||||||
formConfig.evaluationSubstitutionType,
|
formConfig.evaluationSubstitutionType,
|
||||||
);
|
);
|
||||||
} else if (formConfig.controlType === "SORTING") {
|
} else if (formConfig.controlType === formControlTypes.SORTING) {
|
||||||
const actionValue = _.get(action, formConfig.configProperty);
|
const actionValue = _.get(action, formConfig.configProperty);
|
||||||
if (Array.isArray(actionValue)) {
|
if (Array.isArray(actionValue)) {
|
||||||
actionValue.forEach((fieldConfig: any, index: number) => {
|
actionValue.forEach((fieldConfig: any, index: number) => {
|
||||||
|
|
@ -175,7 +186,7 @@ export const getBindingPathsOfAction = (
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (formConfig.controlType === "ENTITY_SELECTOR") {
|
} else if (formConfig.controlType === formControlTypes.ENTITY_SELECTOR) {
|
||||||
if (Array.isArray(formConfig.schema)) {
|
if (Array.isArray(formConfig.schema)) {
|
||||||
formConfig.schema.forEach((schemaField: any, index: number) => {
|
formConfig.schema.forEach((schemaField: any, index: number) => {
|
||||||
if (allowedControlTypes.includes(schemaField.controlType)) {
|
if (allowedControlTypes.includes(schemaField.controlType)) {
|
||||||
|
|
@ -193,7 +204,11 @@ export const getBindingPathsOfAction = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
formConfig.forEach(recursiveFindBindingPaths);
|
formConfig.forEach(recursiveFindBindingPaths);
|
||||||
return bindingPaths;
|
reactivePaths = {
|
||||||
|
...reactivePaths,
|
||||||
|
...bindingPaths,
|
||||||
|
};
|
||||||
|
return { reactivePaths, bindingPaths };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBindingOrConfigPathsForSortingControl = (
|
export const getBindingOrConfigPathsForSortingControl = (
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { DependencyMap, DynamicPath } from "utils/DynamicBindingUtils";
|
||||||
import { DataTreeAction, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
import { DataTreeAction, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||||
import { ActionData } from "reducers/entityReducers/actionsReducer";
|
import { ActionData } from "reducers/entityReducers/actionsReducer";
|
||||||
import {
|
import {
|
||||||
getBindingPathsOfAction,
|
getBindingAndReactivePathsOfAction,
|
||||||
getDataTreeActionConfigPath,
|
getDataTreeActionConfigPath,
|
||||||
} from "entities/Action/actionProperties";
|
} from "entities/Action/actionProperties";
|
||||||
|
|
||||||
|
|
@ -38,6 +38,11 @@ export const generateDataTreeAction = (
|
||||||
getDataTreeActionConfigPath,
|
getDataTreeActionConfigPath,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { bindingPaths, reactivePaths } = getBindingAndReactivePathsOfAction(
|
||||||
|
action.config,
|
||||||
|
editorConfig,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
run: {},
|
run: {},
|
||||||
clear: {},
|
clear: {},
|
||||||
|
|
@ -55,7 +60,8 @@ export const generateDataTreeAction = (
|
||||||
},
|
},
|
||||||
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||||
isLoading: action.isLoading,
|
isLoading: action.isLoading,
|
||||||
bindingPaths: getBindingPathsOfAction(action.config, editorConfig),
|
bindingPaths,
|
||||||
|
reactivePaths,
|
||||||
dependencyMap,
|
dependencyMap,
|
||||||
logBlackList: {},
|
logBlackList: {},
|
||||||
datasourceUrl,
|
datasourceUrl,
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ export interface DataTreeAction
|
||||||
| Record<string, unknown>;
|
| Record<string, unknown>;
|
||||||
dynamicBindingPathList: DynamicPath[];
|
dynamicBindingPathList: DynamicPath[];
|
||||||
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
||||||
|
reactivePaths: Record<string, EvaluationSubstitutionType>;
|
||||||
ENTITY_TYPE: ENTITY_TYPE.ACTION;
|
ENTITY_TYPE: ENTITY_TYPE.ACTION;
|
||||||
dependencyMap: DependencyMap;
|
dependencyMap: DependencyMap;
|
||||||
logBlackList: Record<string, true>;
|
logBlackList: Record<string, true>;
|
||||||
|
|
@ -75,6 +76,7 @@ export interface DataTreeJSAction {
|
||||||
meta: Record<string, MetaArgs>;
|
meta: Record<string, MetaArgs>;
|
||||||
dynamicBindingPathList: DynamicPath[];
|
dynamicBindingPathList: DynamicPath[];
|
||||||
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
||||||
|
reactivePaths: Record<string, EvaluationSubstitutionType>;
|
||||||
variables: Array<string>;
|
variables: Array<string>;
|
||||||
dependencyMap: DependencyMap;
|
dependencyMap: DependencyMap;
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +108,7 @@ export type PropertyOverrideDependency = Record<
|
||||||
|
|
||||||
export interface DataTreeWidget extends WidgetProps {
|
export interface DataTreeWidget extends WidgetProps {
|
||||||
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
||||||
|
reactivePaths: Record<string, EvaluationSubstitutionType>;
|
||||||
triggerPaths: Record<string, boolean>;
|
triggerPaths: Record<string, boolean>;
|
||||||
validationPaths: Record<string, ValidationConfig>;
|
validationPaths: Record<string, ValidationConfig>;
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET;
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer";
|
||||||
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
||||||
import { DependencyMap } from "utils/DynamicBindingUtils";
|
import { DependencyMap } from "utils/DynamicBindingUtils";
|
||||||
|
|
||||||
|
const reg = /this\./g;
|
||||||
|
|
||||||
export const generateDataTreeJSAction = (
|
export const generateDataTreeJSAction = (
|
||||||
js: JSCollectionData,
|
js: JSCollectionData,
|
||||||
): DataTreeJSAction => {
|
): DataTreeJSAction => {
|
||||||
|
|
@ -17,7 +19,7 @@ export const generateDataTreeJSAction = (
|
||||||
const variables = js.config.variables;
|
const variables = js.config.variables;
|
||||||
const listVariables: Array<string> = [];
|
const listVariables: Array<string> = [];
|
||||||
dynamicBindingPathList.push({ key: "body" });
|
dynamicBindingPathList.push({ key: "body" });
|
||||||
const reg = /this\./g;
|
|
||||||
const removeThisReference = js.config.body.replace(reg, `${js.config.name}.`);
|
const removeThisReference = js.config.body.replace(reg, `${js.config.name}.`);
|
||||||
bindingPaths["body"] = EvaluationSubstitutionType.SMART_SUBSTITUTE;
|
bindingPaths["body"] = EvaluationSubstitutionType.SMART_SUBSTITUTE;
|
||||||
|
|
||||||
|
|
@ -56,7 +58,8 @@ export const generateDataTreeJSAction = (
|
||||||
ENTITY_TYPE: ENTITY_TYPE.JSACTION,
|
ENTITY_TYPE: ENTITY_TYPE.JSACTION,
|
||||||
body: removeThisReference,
|
body: removeThisReference,
|
||||||
meta: meta,
|
meta: meta,
|
||||||
bindingPaths: bindingPaths,
|
bindingPaths: bindingPaths, // As all js object function referred to as action is user javascript code, we add them as binding paths.
|
||||||
|
reactivePaths: { ...bindingPaths },
|
||||||
dynamicBindingPathList: dynamicBindingPathList,
|
dynamicBindingPathList: dynamicBindingPathList,
|
||||||
variables: listVariables,
|
variables: listVariables,
|
||||||
dependencyMap: dependencyMap,
|
dependencyMap: dependencyMap,
|
||||||
|
|
|
||||||
|
|
@ -196,19 +196,24 @@ describe("generateDataTreeWidget", () => {
|
||||||
isDirty: true,
|
isDirty: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const expected: DataTreeWidget = {
|
const bindingPaths = {
|
||||||
bindingPaths: {
|
|
||||||
defaultText: EvaluationSubstitutionType.TEMPLATE,
|
defaultText: EvaluationSubstitutionType.TEMPLATE,
|
||||||
errorMessage: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isDirty: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isDisabled: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isFocused: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isRequired: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isValid: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isVisible: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
placeholderText: EvaluationSubstitutionType.TEMPLATE,
|
placeholderText: EvaluationSubstitutionType.TEMPLATE,
|
||||||
regex: EvaluationSubstitutionType.TEMPLATE,
|
regex: EvaluationSubstitutionType.TEMPLATE,
|
||||||
resetOnSubmit: EvaluationSubstitutionType.TEMPLATE,
|
resetOnSubmit: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isVisible: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isRequired: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isDisabled: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
errorMessage: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
};
|
||||||
|
|
||||||
|
const expected: DataTreeWidget = {
|
||||||
|
bindingPaths,
|
||||||
|
reactivePaths: {
|
||||||
|
...bindingPaths,
|
||||||
|
isDirty: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isFocused: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isValid: EvaluationSubstitutionType.TEMPLATE,
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
value: EvaluationSubstitutionType.TEMPLATE,
|
value: EvaluationSubstitutionType.TEMPLATE,
|
||||||
"meta.text": EvaluationSubstitutionType.TEMPLATE,
|
"meta.text": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ export const generateDataTreeWidget = (
|
||||||
|
|
||||||
const {
|
const {
|
||||||
bindingPaths,
|
bindingPaths,
|
||||||
|
reactivePaths,
|
||||||
triggerPaths,
|
triggerPaths,
|
||||||
validationPaths,
|
validationPaths,
|
||||||
} = getAllPathsFromPropertyConfig(widget, propertyPaneConfigs, {
|
} = getAllPathsFromPropertyConfig(widget, propertyPaneConfigs, {
|
||||||
|
|
@ -152,6 +153,7 @@ export const generateDataTreeWidget = (
|
||||||
propertyOverrideDependency,
|
propertyOverrideDependency,
|
||||||
overridingPropertyPaths,
|
overridingPropertyPaths,
|
||||||
bindingPaths,
|
bindingPaths,
|
||||||
|
reactivePaths,
|
||||||
triggerPaths,
|
triggerPaths,
|
||||||
validationPaths,
|
validationPaths,
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,61 @@ describe("getAllPathsFromPropertyConfig", () => {
|
||||||
};
|
};
|
||||||
const config = tablePropertyPaneConfig;
|
const config = tablePropertyPaneConfig;
|
||||||
|
|
||||||
|
const bindingPaths = {
|
||||||
|
tableData: EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
||||||
|
defaultSearchText: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
defaultSelectedRow: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isVisible: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isSortable: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
animateLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
primaryColumnId: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
compactMode: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isVisibleDownload: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isVisibleFilters: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isVisiblePagination: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isVisibleSearch: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
delimiter: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.name.computedValue": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.name.horizontalAlignment":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.name.verticalAlignment":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.name.textSize": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.name.fontStyle": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.name.textColor": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
// "primaryColumns.name.isVisible": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.name.isCellVisible": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
||||||
|
"primaryColumns.name.cellBackground": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.inputFormat":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.outputFormat":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.computedValue":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.isCellVisible":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.horizontalAlignment":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.verticalAlignment":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.textSize": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.fontStyle": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.textColor": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.createdAt.cellBackground":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.status.buttonLabel": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.status.buttonColor": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.status.isDisabled": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.status.buttonVariant":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
"primaryColumns.status.isCellVisible":
|
||||||
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
};
|
||||||
const expected = {
|
const expected = {
|
||||||
bindingPaths: {
|
bindingPaths,
|
||||||
|
reactivePaths: {
|
||||||
|
...bindingPaths,
|
||||||
selectedRow: EvaluationSubstitutionType.TEMPLATE,
|
selectedRow: EvaluationSubstitutionType.TEMPLATE,
|
||||||
selectedRows: EvaluationSubstitutionType.TEMPLATE,
|
selectedRows: EvaluationSubstitutionType.TEMPLATE,
|
||||||
tableData: EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
tableData: EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
||||||
|
|
@ -171,8 +224,6 @@ describe("getAllPathsFromPropertyConfig", () => {
|
||||||
EvaluationSubstitutionType.TEMPLATE,
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
"primaryColumns.status.buttonLabel":
|
"primaryColumns.status.buttonLabel":
|
||||||
EvaluationSubstitutionType.TEMPLATE,
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
"primaryColumns.status.buttonVariant":
|
|
||||||
EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
"primaryColumns.status.buttonColor":
|
"primaryColumns.status.buttonColor":
|
||||||
EvaluationSubstitutionType.TEMPLATE,
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
"primaryColumns.status.isDisabled": EvaluationSubstitutionType.TEMPLATE,
|
"primaryColumns.status.isDisabled": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -508,8 +559,7 @@ describe("getAllPathsFromPropertyConfig", () => {
|
||||||
};
|
};
|
||||||
const config = chartPorpertyConfig;
|
const config = chartPorpertyConfig;
|
||||||
|
|
||||||
const expected = {
|
const bindingPaths = {
|
||||||
bindingPaths: {
|
|
||||||
chartType: EvaluationSubstitutionType.TEMPLATE,
|
chartType: EvaluationSubstitutionType.TEMPLATE,
|
||||||
chartName: EvaluationSubstitutionType.TEMPLATE,
|
chartName: EvaluationSubstitutionType.TEMPLATE,
|
||||||
"chartData.random-id.seriesName": EvaluationSubstitutionType.TEMPLATE,
|
"chartData.random-id.seriesName": EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -519,7 +569,11 @@ describe("getAllPathsFromPropertyConfig", () => {
|
||||||
isVisible: EvaluationSubstitutionType.TEMPLATE,
|
isVisible: EvaluationSubstitutionType.TEMPLATE,
|
||||||
animateLoading: EvaluationSubstitutionType.TEMPLATE,
|
animateLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
setAdaptiveYMin: EvaluationSubstitutionType.TEMPLATE,
|
setAdaptiveYMin: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
const expected = {
|
||||||
|
bindingPaths,
|
||||||
|
reactivePaths: { ...bindingPaths },
|
||||||
triggerPaths: {
|
triggerPaths: {
|
||||||
onDataPointClick: true,
|
onDataPointClick: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,21 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Paths
|
* @typedef {Object} Paths
|
||||||
* @property {Object} configBindingPaths - The Binding Path
|
* @property {Object} configBindingPaths - The Binding Path
|
||||||
|
* @property {Object} configReactivePaths - The Dynamic Property Path
|
||||||
* @property {Object} configTriggerPaths - The Trigger Path
|
* @property {Object} configTriggerPaths - The Trigger Path
|
||||||
* @property {Object} configValidationPaths - The Validation Path
|
* @property {Object} configValidationPaths - The Validation Path
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All widget's property or paths where user can write javaScript bindings using mustache syntax are called bindingPaths.
|
||||||
|
* Widget's meta and derived paths aren't binding paths as user can't add or remove binding for those value.
|
||||||
|
*/
|
||||||
|
type BindingPaths = Record<string, EvaluationSubstitutionType>;
|
||||||
|
/**
|
||||||
|
* Binding paths and non-binding paths of widget/action together form reactivePaths.
|
||||||
|
*/
|
||||||
|
type ReactivePaths = Record<string, EvaluationSubstitutionType>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function gets the binding validation and trigger paths from a config
|
* This function gets the binding validation and trigger paths from a config
|
||||||
* @param config
|
* @param config
|
||||||
|
|
@ -24,14 +35,14 @@ const checkPathsInConfig = (
|
||||||
config: any,
|
config: any,
|
||||||
path: string,
|
path: string,
|
||||||
): {
|
): {
|
||||||
configBindingPaths: Record<string, EvaluationSubstitutionType>;
|
configBindingPaths: BindingPaths;
|
||||||
|
configReactivePaths: ReactivePaths;
|
||||||
configTriggerPaths: Record<string, true>;
|
configTriggerPaths: Record<string, true>;
|
||||||
configValidationPaths: Record<string, ValidationConfig>;
|
configValidationPaths: Record<string, ValidationConfig>;
|
||||||
} => {
|
} => {
|
||||||
const configBindingPaths: Record<string, EvaluationSubstitutionType> = {};
|
const configBindingPaths: BindingPaths = {};
|
||||||
const configTriggerPaths: Record<string, true> = {};
|
const configTriggerPaths: Record<string, true> = {};
|
||||||
const configValidationPaths: Record<any, ValidationConfig> = {};
|
const configValidationPaths: Record<any, ValidationConfig> = {};
|
||||||
|
|
||||||
// Purely a Binding Path
|
// Purely a Binding Path
|
||||||
if (config.isBindProperty && !config.isTriggerProperty) {
|
if (config.isBindProperty && !config.isTriggerProperty) {
|
||||||
configBindingPaths[path] =
|
configBindingPaths[path] =
|
||||||
|
|
@ -42,7 +53,12 @@ const checkPathsInConfig = (
|
||||||
} else if (config.isBindProperty && config.isTriggerProperty) {
|
} else if (config.isBindProperty && config.isTriggerProperty) {
|
||||||
configTriggerPaths[path] = true;
|
configTriggerPaths[path] = true;
|
||||||
}
|
}
|
||||||
return { configBindingPaths, configTriggerPaths, configValidationPaths };
|
return {
|
||||||
|
configBindingPaths,
|
||||||
|
configReactivePaths: configBindingPaths, // All bindingPaths are reactivePaths.
|
||||||
|
configTriggerPaths,
|
||||||
|
configValidationPaths,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// "originalWidget" param here always contains the complete widget props
|
// "originalWidget" param here always contains the complete widget props
|
||||||
|
|
@ -55,7 +71,9 @@ const childHasPanelConfig = (
|
||||||
) => {
|
) => {
|
||||||
const panelPropertyPath = config.propertyName;
|
const panelPropertyPath = config.propertyName;
|
||||||
const widgetPanelPropertyValues = get(widget, panelPropertyPath);
|
const widgetPanelPropertyValues = get(widget, panelPropertyPath);
|
||||||
let bindingPaths: Record<string, EvaluationSubstitutionType> = {};
|
|
||||||
|
let bindingPaths: BindingPaths = {};
|
||||||
|
let reactivePaths: ReactivePaths = {};
|
||||||
let triggerPaths: Record<string, true> = {};
|
let triggerPaths: Record<string, true> = {};
|
||||||
let validationPaths: Record<any, ValidationConfig> = {};
|
let validationPaths: Record<any, ValidationConfig> = {};
|
||||||
if (widgetPanelPropertyValues) {
|
if (widgetPanelPropertyValues) {
|
||||||
|
|
@ -86,13 +104,21 @@ const childHasPanelConfig = (
|
||||||
if (!isControlHidden) {
|
if (!isControlHidden) {
|
||||||
const {
|
const {
|
||||||
configBindingPaths,
|
configBindingPaths,
|
||||||
|
configReactivePaths,
|
||||||
configTriggerPaths,
|
configTriggerPaths,
|
||||||
configValidationPaths,
|
configValidationPaths,
|
||||||
} = checkPathsInConfig(
|
} = checkPathsInConfig(
|
||||||
panelColumnControlConfig,
|
panelColumnControlConfig,
|
||||||
panelPropertyConfigPath,
|
panelPropertyConfigPath,
|
||||||
);
|
);
|
||||||
bindingPaths = { ...configBindingPaths, ...bindingPaths };
|
bindingPaths = {
|
||||||
|
...configBindingPaths,
|
||||||
|
...bindingPaths,
|
||||||
|
};
|
||||||
|
reactivePaths = {
|
||||||
|
...configReactivePaths,
|
||||||
|
...reactivePaths,
|
||||||
|
};
|
||||||
triggerPaths = { ...configTriggerPaths, ...triggerPaths };
|
triggerPaths = { ...configTriggerPaths, ...triggerPaths };
|
||||||
validationPaths = {
|
validationPaths = {
|
||||||
...configValidationPaths,
|
...configValidationPaths,
|
||||||
|
|
@ -102,6 +128,7 @@ const childHasPanelConfig = (
|
||||||
if (panelColumnControlConfig.panelConfig) {
|
if (panelColumnControlConfig.panelConfig) {
|
||||||
const {
|
const {
|
||||||
bindingPaths: panelBindingPaths,
|
bindingPaths: panelBindingPaths,
|
||||||
|
reactivePaths: panelReactivePaths,
|
||||||
triggerPaths: panelTriggerPaths,
|
triggerPaths: panelTriggerPaths,
|
||||||
validationPaths: panelValidationPaths,
|
validationPaths: panelValidationPaths,
|
||||||
} = childHasPanelConfig(
|
} = childHasPanelConfig(
|
||||||
|
|
@ -110,7 +137,14 @@ const childHasPanelConfig = (
|
||||||
panelPropertyConfigPath,
|
panelPropertyConfigPath,
|
||||||
originalWidget,
|
originalWidget,
|
||||||
);
|
);
|
||||||
bindingPaths = { ...panelBindingPaths, ...bindingPaths };
|
bindingPaths = {
|
||||||
|
...panelBindingPaths,
|
||||||
|
...bindingPaths,
|
||||||
|
};
|
||||||
|
reactivePaths = {
|
||||||
|
...panelReactivePaths,
|
||||||
|
...reactivePaths,
|
||||||
|
};
|
||||||
triggerPaths = { ...panelTriggerPaths, ...triggerPaths };
|
triggerPaths = { ...panelTriggerPaths, ...triggerPaths };
|
||||||
validationPaths = {
|
validationPaths = {
|
||||||
...panelValidationPaths,
|
...panelValidationPaths,
|
||||||
|
|
@ -126,7 +160,7 @@ const childHasPanelConfig = (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { bindingPaths, triggerPaths, validationPaths };
|
return { reactivePaths, triggerPaths, validationPaths, bindingPaths };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAllPathsFromPropertyConfig = (
|
export const getAllPathsFromPropertyConfig = (
|
||||||
|
|
@ -134,15 +168,16 @@ export const getAllPathsFromPropertyConfig = (
|
||||||
widgetConfig: readonly PropertyPaneConfig[],
|
widgetConfig: readonly PropertyPaneConfig[],
|
||||||
defaultProperties: Record<string, any>,
|
defaultProperties: Record<string, any>,
|
||||||
): {
|
): {
|
||||||
bindingPaths: Record<string, EvaluationSubstitutionType>;
|
bindingPaths: BindingPaths;
|
||||||
|
reactivePaths: ReactivePaths;
|
||||||
triggerPaths: Record<string, true>;
|
triggerPaths: Record<string, true>;
|
||||||
validationPaths: Record<string, ValidationConfig>;
|
validationPaths: Record<string, ValidationConfig>;
|
||||||
} => {
|
} => {
|
||||||
let bindingPaths: Record<string, EvaluationSubstitutionType> = {};
|
let bindingPaths: BindingPaths = {};
|
||||||
Object.keys(defaultProperties).forEach(
|
let reactivePaths: ReactivePaths = {};
|
||||||
(property) =>
|
Object.keys(defaultProperties).forEach((property) => {
|
||||||
(bindingPaths[property] = EvaluationSubstitutionType.TEMPLATE),
|
reactivePaths[property] = EvaluationSubstitutionType.TEMPLATE;
|
||||||
);
|
});
|
||||||
let triggerPaths: Record<string, true> = {};
|
let triggerPaths: Record<string, true> = {};
|
||||||
let validationPaths: Record<any, ValidationConfig> = {};
|
let validationPaths: Record<any, ValidationConfig> = {};
|
||||||
|
|
||||||
|
|
@ -158,11 +193,19 @@ export const getAllPathsFromPropertyConfig = (
|
||||||
const path = controlConfig.propertyName;
|
const path = controlConfig.propertyName;
|
||||||
const {
|
const {
|
||||||
configBindingPaths,
|
configBindingPaths,
|
||||||
|
configReactivePaths,
|
||||||
configTriggerPaths,
|
configTriggerPaths,
|
||||||
configValidationPaths,
|
configValidationPaths,
|
||||||
} = checkPathsInConfig(controlConfig, path);
|
} = checkPathsInConfig(controlConfig, path);
|
||||||
|
bindingPaths = {
|
||||||
|
...bindingPaths,
|
||||||
|
...configBindingPaths,
|
||||||
|
};
|
||||||
// Update default path configs with the ones in the property config
|
// Update default path configs with the ones in the property config
|
||||||
bindingPaths = { ...bindingPaths, ...configBindingPaths };
|
reactivePaths = {
|
||||||
|
...reactivePaths,
|
||||||
|
...configReactivePaths,
|
||||||
|
};
|
||||||
triggerPaths = { ...triggerPaths, ...configTriggerPaths };
|
triggerPaths = { ...triggerPaths, ...configTriggerPaths };
|
||||||
validationPaths = { ...validationPaths, ...configValidationPaths };
|
validationPaths = { ...validationPaths, ...configValidationPaths };
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +217,14 @@ export const getAllPathsFromPropertyConfig = (
|
||||||
basePath,
|
basePath,
|
||||||
widget,
|
widget,
|
||||||
);
|
);
|
||||||
bindingPaths = { ...bindingPaths, ...resultingPaths.bindingPaths };
|
bindingPaths = {
|
||||||
|
...bindingPaths,
|
||||||
|
...resultingPaths.bindingPaths,
|
||||||
|
};
|
||||||
|
reactivePaths = {
|
||||||
|
...reactivePaths,
|
||||||
|
...resultingPaths.reactivePaths,
|
||||||
|
};
|
||||||
triggerPaths = { ...triggerPaths, ...resultingPaths.triggerPaths };
|
triggerPaths = { ...triggerPaths, ...resultingPaths.triggerPaths };
|
||||||
validationPaths = {
|
validationPaths = {
|
||||||
...validationPaths,
|
...validationPaths,
|
||||||
|
|
@ -195,13 +245,21 @@ export const getAllPathsFromPropertyConfig = (
|
||||||
const childArrayPropertyPath = `${objectIndexPropertyPath}.${childPropertyConfig.propertyName}`;
|
const childArrayPropertyPath = `${objectIndexPropertyPath}.${childPropertyConfig.propertyName}`;
|
||||||
const {
|
const {
|
||||||
configBindingPaths,
|
configBindingPaths,
|
||||||
|
configReactivePaths,
|
||||||
configTriggerPaths,
|
configTriggerPaths,
|
||||||
configValidationPaths,
|
configValidationPaths,
|
||||||
} = checkPathsInConfig(
|
} = checkPathsInConfig(
|
||||||
childPropertyConfig,
|
childPropertyConfig,
|
||||||
childArrayPropertyPath,
|
childArrayPropertyPath,
|
||||||
);
|
);
|
||||||
bindingPaths = { ...bindingPaths, ...configBindingPaths };
|
bindingPaths = {
|
||||||
|
...bindingPaths,
|
||||||
|
...configBindingPaths,
|
||||||
|
};
|
||||||
|
reactivePaths = {
|
||||||
|
...reactivePaths,
|
||||||
|
...configReactivePaths,
|
||||||
|
};
|
||||||
triggerPaths = { ...triggerPaths, ...configTriggerPaths };
|
triggerPaths = { ...triggerPaths, ...configTriggerPaths };
|
||||||
validationPaths = {
|
validationPaths = {
|
||||||
...validationPaths,
|
...validationPaths,
|
||||||
|
|
@ -215,7 +273,7 @@ export const getAllPathsFromPropertyConfig = (
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { bindingPaths, triggerPaths, validationPaths };
|
return { reactivePaths, triggerPaths, validationPaths, bindingPaths };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
} from "components/formControls/utils";
|
} from "components/formControls/utils";
|
||||||
import { useSelector, shallowEqual } from "react-redux";
|
import { useSelector, shallowEqual } from "react-redux";
|
||||||
import { getFormValues } from "redux-form";
|
import { getFormValues } from "redux-form";
|
||||||
import FormControlFactory from "utils/FormControlFactory";
|
import FormControlFactory from "utils/formControl/FormControlFactory";
|
||||||
|
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import { Action } from "entities/Action";
|
import { Action } from "entities/Action";
|
||||||
|
|
|
||||||
|
|
@ -448,6 +448,7 @@ const createLoadingWidget = (
|
||||||
type: WidgetTypes.SKELETON_WIDGET,
|
type: WidgetTypes.SKELETON_WIDGET,
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
triggerPaths: {},
|
triggerPaths: {},
|
||||||
validationPaths: {},
|
validationPaths: {},
|
||||||
logBlackList: {},
|
logBlackList: {},
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
import { getAppsmithConfigs } from "@appsmith/configs";
|
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import AnalyticsUtil from "./AnalyticsUtil";
|
import AnalyticsUtil from "./AnalyticsUtil";
|
||||||
import FormControlRegistry from "./FormControlRegistry";
|
import FormControlRegistry from "./formControl/FormControlRegistry";
|
||||||
import { Property } from "api/ActionAPI";
|
import { Property } from "api/ActionAPI";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Action, PluginType } from "entities/Action";
|
import { Action, PluginType } from "entities/Action";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { getPropertyPath } from "./DynamicBindingUtils";
|
||||||
import {
|
import {
|
||||||
EVAL_VALUE_PATH,
|
EVAL_VALUE_PATH,
|
||||||
getDynamicBindingsChangesSaga,
|
getDynamicBindingsChangesSaga,
|
||||||
|
|
@ -156,6 +157,23 @@ describe("DynamicBindingPathlist", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getPropertyPath function", () => {
|
||||||
|
it("test getPropertyPath", () => {
|
||||||
|
const testCases = [
|
||||||
|
["Table1.searchText", "searchText"],
|
||||||
|
["Table1.selectedRow", "selectedRow"],
|
||||||
|
["Table1.meta.searchText", "meta.searchText"],
|
||||||
|
["Table1", "Table1"],
|
||||||
|
["Table1.", ""],
|
||||||
|
];
|
||||||
|
|
||||||
|
testCases.forEach(([input, expectedResult]) => {
|
||||||
|
const actualResult = getPropertyPath(input);
|
||||||
|
expect(actualResult).toStrictEqual(expectedResult);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("getNestedEvalPath", () => {
|
describe("getNestedEvalPath", () => {
|
||||||
it("returns valid nested path", () => {
|
it("returns valid nested path", () => {
|
||||||
const actualUnpopulatedNestedPath = getEvalValuePath(
|
const actualUnpopulatedNestedPath = getEvalValuePath(
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,15 @@ export const isPathADynamicBinding = (
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Get property path from full property path
|
||||||
|
* Input: "Table1.meta.searchText" => Output: "meta.searchText"
|
||||||
|
* @param {string} fullPropertyPath
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
export const getPropertyPath = (fullPropertyPath: string) => {
|
||||||
|
return fullPropertyPath.substring(fullPropertyPath.indexOf(".") + 1);
|
||||||
|
};
|
||||||
|
|
||||||
export const getWidgetDynamicTriggerPathList = (
|
export const getWidgetDynamicTriggerPathList = (
|
||||||
widget: WidgetProps,
|
widget: WidgetProps,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ const JS_object_tree: DataTreeJSAction = {
|
||||||
meta: {},
|
meta: {},
|
||||||
dynamicBindingPathList: [],
|
dynamicBindingPathList: [],
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
variables: [],
|
variables: [],
|
||||||
dependencyMap: {},
|
dependencyMap: {},
|
||||||
};
|
};
|
||||||
|
|
@ -26,6 +27,7 @@ const JS_object_tree: DataTreeJSAction = {
|
||||||
const Select_tree: DataTreeWidget = {
|
const Select_tree: DataTreeWidget = {
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
triggerPaths: {},
|
triggerPaths: {},
|
||||||
validationPaths: {},
|
validationPaths: {},
|
||||||
logBlackList: {},
|
logBlackList: {},
|
||||||
|
|
@ -58,6 +60,7 @@ const Query_tree: DataTreeAction = {
|
||||||
clear: {},
|
clear: {},
|
||||||
dynamicBindingPathList: [],
|
dynamicBindingPathList: [],
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||||
dependencyMap: {},
|
dependencyMap: {},
|
||||||
logBlackList: {},
|
logBlackList: {},
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ describe("dataTreeTypeDefCreator", () => {
|
||||||
bindingPaths: {
|
bindingPaths: {
|
||||||
defaultText: EvaluationSubstitutionType.TEMPLATE,
|
defaultText: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
|
reactivePaths: {
|
||||||
|
defaultText: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
},
|
||||||
triggerPaths: {
|
triggerPaths: {
|
||||||
onTextChange: true,
|
onTextChange: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -45,50 +45,54 @@ import SortingControl, {
|
||||||
import EntitySelectorControl, {
|
import EntitySelectorControl, {
|
||||||
EntitySelectorControlProps,
|
EntitySelectorControlProps,
|
||||||
} from "components/formControls/EntitySelectorControl";
|
} from "components/formControls/EntitySelectorControl";
|
||||||
|
import formControlTypes from "./formControlTypes";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: If you are adding a component that uses FormControl
|
* NOTE: If you are adding a component that uses FormControl
|
||||||
* then add logic for creating bindingPaths in recursiveFindBindingPaths() at entities/Action/actionProperties.ts
|
* then add logic for creating reactivePaths in recursiveFindReactivePaths() at entities/Action/actionProperties.ts
|
||||||
*/
|
*/
|
||||||
class FormControlRegistry {
|
class FormControlRegistry {
|
||||||
static registerFormControlBuilders() {
|
static registerFormControlBuilders() {
|
||||||
FormControlFactory.registerControlBuilder("INPUT_TEXT", {
|
FormControlFactory.registerControlBuilder(formControlTypes.INPUT_TEXT, {
|
||||||
buildPropertyControl(controlProps: InputControlProps): JSX.Element {
|
buildPropertyControl(controlProps: InputControlProps): JSX.Element {
|
||||||
return <InputTextControl {...controlProps} />;
|
return <InputTextControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("FIXED_KEY_INPUT", {
|
FormControlFactory.registerControlBuilder(
|
||||||
|
formControlTypes.FIXED_KEY_INPUT,
|
||||||
|
{
|
||||||
buildPropertyControl(
|
buildPropertyControl(
|
||||||
controlProps: FixedKeyInputControlProps,
|
controlProps: FixedKeyInputControlProps,
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
//TODO: may not be in use
|
//TODO: may not be in use
|
||||||
return <FixedKeyInputControl {...controlProps} />;
|
return <FixedKeyInputControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
FormControlFactory.registerControlBuilder("DROP_DOWN", {
|
);
|
||||||
|
FormControlFactory.registerControlBuilder(formControlTypes.DROP_DOWN, {
|
||||||
buildPropertyControl(controlProps: DropDownControlProps): JSX.Element {
|
buildPropertyControl(controlProps: DropDownControlProps): JSX.Element {
|
||||||
return <DropDownControl {...controlProps} />;
|
return <DropDownControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("SWITCH", {
|
FormControlFactory.registerControlBuilder(formControlTypes.SWITCH, {
|
||||||
buildPropertyControl(controlProps: SwitchControlProps): JSX.Element {
|
buildPropertyControl(controlProps: SwitchControlProps): JSX.Element {
|
||||||
return <SwitchControl {...controlProps} />;
|
return <SwitchControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("KEYVALUE_ARRAY", {
|
FormControlFactory.registerControlBuilder(formControlTypes.KEYVALUE_ARRAY, {
|
||||||
buildPropertyControl(
|
buildPropertyControl(
|
||||||
controlProps: KeyValueArrayControlProps,
|
controlProps: KeyValueArrayControlProps,
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
return <KeyValueArrayControl {...controlProps} />;
|
return <KeyValueArrayControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("FILE_PICKER", {
|
FormControlFactory.registerControlBuilder(formControlTypes.FILE_PICKER, {
|
||||||
buildPropertyControl(controlProps: FilePickerControlProps): JSX.Element {
|
buildPropertyControl(controlProps: FilePickerControlProps): JSX.Element {
|
||||||
//used by redshift datasource
|
//used by redshift datasource
|
||||||
return <FilePickerControl {...controlProps} />;
|
return <FilePickerControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("KEY_VAL_INPUT", {
|
FormControlFactory.registerControlBuilder(formControlTypes.KEY_VAL_INPUT, {
|
||||||
//TODO: may not be in use, replace it with KeyValueArrayControl
|
//TODO: may not be in use, replace it with KeyValueArrayControl
|
||||||
buildPropertyControl(
|
buildPropertyControl(
|
||||||
controlProps: KeyValueInputControlProps,
|
controlProps: KeyValueInputControlProps,
|
||||||
|
|
@ -96,58 +100,67 @@ class FormControlRegistry {
|
||||||
return <KeyValueInputControl {...controlProps} />;
|
return <KeyValueInputControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("QUERY_DYNAMIC_TEXT", {
|
FormControlFactory.registerControlBuilder(
|
||||||
|
formControlTypes.QUERY_DYNAMIC_TEXT,
|
||||||
|
{
|
||||||
buildPropertyControl(controlProps: DynamicTextFieldProps): JSX.Element {
|
buildPropertyControl(controlProps: DynamicTextFieldProps): JSX.Element {
|
||||||
return <DynamicTextControl {...controlProps} />;
|
return <DynamicTextControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
FormControlFactory.registerControlBuilder("QUERY_DYNAMIC_INPUT_TEXT", {
|
);
|
||||||
|
FormControlFactory.registerControlBuilder(
|
||||||
|
formControlTypes.QUERY_DYNAMIC_INPUT_TEXT,
|
||||||
|
{
|
||||||
buildPropertyControl(
|
buildPropertyControl(
|
||||||
controlProps: DynamicInputControlProps,
|
controlProps: DynamicInputControlProps,
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
return <DynamicInputTextControl {...controlProps} />;
|
return <DynamicInputTextControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
FormControlFactory.registerControlBuilder("CHECKBOX", {
|
);
|
||||||
|
FormControlFactory.registerControlBuilder(formControlTypes.CHECKBOX, {
|
||||||
buildPropertyControl(controlProps: CheckboxControlProps): JSX.Element {
|
buildPropertyControl(controlProps: CheckboxControlProps): JSX.Element {
|
||||||
//used in API datasource form only
|
//used in API datasource form only
|
||||||
return <CheckboxControl {...controlProps} />;
|
return <CheckboxControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("NUMBER_INPUT", {
|
FormControlFactory.registerControlBuilder(formControlTypes.NUMBER_INPUT, {
|
||||||
buildPropertyControl(controlProps: InputControlProps): JSX.Element {
|
buildPropertyControl(controlProps: InputControlProps): JSX.Element {
|
||||||
return <InputTextControl {...controlProps} />;
|
return <InputTextControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("ARRAY_FIELD", {
|
FormControlFactory.registerControlBuilder(formControlTypes.ARRAY_FIELD, {
|
||||||
buildPropertyControl(controlProps: FieldArrayControlProps): JSX.Element {
|
buildPropertyControl(controlProps: FieldArrayControlProps): JSX.Element {
|
||||||
return <FieldArrayControl {...controlProps} />;
|
return <FieldArrayControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("WHERE_CLAUSE", {
|
FormControlFactory.registerControlBuilder(formControlTypes.WHERE_CLAUSE, {
|
||||||
buildPropertyControl(controlProps: WhereClauseControlProps): JSX.Element {
|
buildPropertyControl(controlProps: WhereClauseControlProps): JSX.Element {
|
||||||
return <WhereClauseControl {...controlProps} />;
|
return <WhereClauseControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("ENTITY_SELECTOR", {
|
FormControlFactory.registerControlBuilder(
|
||||||
|
formControlTypes.ENTITY_SELECTOR,
|
||||||
|
{
|
||||||
buildPropertyControl(
|
buildPropertyControl(
|
||||||
controlProps: EntitySelectorControlProps,
|
controlProps: EntitySelectorControlProps,
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
return <EntitySelectorControl {...controlProps} />;
|
return <EntitySelectorControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
FormControlFactory.registerControlBuilder("PAGINATION", {
|
FormControlFactory.registerControlBuilder(formControlTypes.PAGINATION, {
|
||||||
buildPropertyControl(controlProps: PaginationControlProps): JSX.Element {
|
buildPropertyControl(controlProps: PaginationControlProps): JSX.Element {
|
||||||
return <PaginationControl {...controlProps} />;
|
return <PaginationControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("SORTING", {
|
FormControlFactory.registerControlBuilder(formControlTypes.SORTING, {
|
||||||
buildPropertyControl(controlProps: SortingControlProps): JSX.Element {
|
buildPropertyControl(controlProps: SortingControlProps): JSX.Element {
|
||||||
return <SortingControl {...controlProps} />;
|
return <SortingControl {...controlProps} />;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
FormControlFactory.registerControlBuilder("PROJECTION", {
|
FormControlFactory.registerControlBuilder(formControlTypes.PROJECTION, {
|
||||||
buildPropertyControl(controlProps: DropDownControlProps): JSX.Element {
|
buildPropertyControl(controlProps: DropDownControlProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<DropDownControl
|
<DropDownControl
|
||||||
19
app/client/src/utils/formControl/formControlTypes.ts
Normal file
19
app/client/src/utils/formControl/formControlTypes.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
export default {
|
||||||
|
INPUT_TEXT: "INPUT_TEXT",
|
||||||
|
FIXED_KEY_INPUT: "FIXED_KEY_INPUT",
|
||||||
|
DROP_DOWN: "DROP_DOWN",
|
||||||
|
SWITCH: "SWITCH",
|
||||||
|
KEYVALUE_ARRAY: "KEYVALUE_ARRAY",
|
||||||
|
FILE_PICKER: "FILE_PICKER",
|
||||||
|
KEY_VAL_INPUT: "KEY_VAL_INPUT",
|
||||||
|
QUERY_DYNAMIC_TEXT: "QUERY_DYNAMIC_TEXT",
|
||||||
|
QUERY_DYNAMIC_INPUT_TEXT: "QUERY_DYNAMIC_INPUT_TEXT",
|
||||||
|
CHECKBOX: "CHECKBOX",
|
||||||
|
NUMBER_INPUT: "NUMBER_INPUT",
|
||||||
|
ARRAY_FIELD: "ARRAY_FIELD",
|
||||||
|
WHERE_CLAUSE: "WHERE_CLAUSE",
|
||||||
|
ENTITY_SELECTOR: "ENTITY_SELECTOR",
|
||||||
|
PAGINATION: "PAGINATION",
|
||||||
|
SORTING: "SORTING",
|
||||||
|
PROJECTION: "PROJECTION",
|
||||||
|
};
|
||||||
|
|
@ -20,3 +20,16 @@ export const mapTree = (tree: Tree, callback: (tree: Tree) => Tree) => {
|
||||||
}
|
}
|
||||||
return { ...mapped };
|
return { ...mapped };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sorts the object's value which is array of string.
|
||||||
|
*
|
||||||
|
* @param {Record<string, Array<string>>} data
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
export const sortObjectWithArray = (data: Record<string, Array<string>>) => {
|
||||||
|
Object.entries(data).map(([key, value]) => {
|
||||||
|
data[key] = value.sort();
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ describe("Add functions", () => {
|
||||||
dynamicBindingPathList: [],
|
dynamicBindingPathList: [],
|
||||||
name: "action1",
|
name: "action1",
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
run: {},
|
run: {},
|
||||||
clear: {},
|
clear: {},
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
getEntityDynamicBindingPathList,
|
getEntityDynamicBindingPathList,
|
||||||
getEvalErrorPath,
|
getEvalErrorPath,
|
||||||
getEvalValuePath,
|
getEvalValuePath,
|
||||||
|
getPropertyPath,
|
||||||
isChildPropertyPath,
|
isChildPropertyPath,
|
||||||
isPathADynamicBinding,
|
isPathADynamicBinding,
|
||||||
isPathADynamicTrigger,
|
isPathADynamicTrigger,
|
||||||
|
|
@ -76,7 +77,7 @@ import { JSUpdate } from "utils/JSPaneUtils";
|
||||||
import {
|
import {
|
||||||
addWidgetPropertyDependencies,
|
addWidgetPropertyDependencies,
|
||||||
overrideWidgetProperties,
|
overrideWidgetProperties,
|
||||||
} from "./evaluationUtils";
|
} from "../evaluationUtils";
|
||||||
import {
|
import {
|
||||||
ActionValidationConfigMap,
|
ActionValidationConfigMap,
|
||||||
ValidationConfig,
|
ValidationConfig,
|
||||||
|
|
@ -292,19 +293,21 @@ export default class DataTreeEvaluator {
|
||||||
translateDiffEventToDataTreeDiffEvent(diff, localUnEvalTree),
|
translateDiffEventToDataTreeDiffEvent(diff, localUnEvalTree),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.logs.push({ differences, translatedDiffs });
|
this.logs.push({
|
||||||
|
differences,
|
||||||
|
translatedDiffs,
|
||||||
|
});
|
||||||
const diffCheckTimeStop = performance.now();
|
const diffCheckTimeStop = performance.now();
|
||||||
// Check if dependencies have changed
|
// Check if dependencies have changed
|
||||||
const updateDependenciesStart = performance.now();
|
const updateDependenciesStart = performance.now();
|
||||||
|
|
||||||
this.logs.push({ differences: clone(differences), translatedDiffs });
|
|
||||||
|
|
||||||
// Find all the paths that have changed as part of the difference and update the
|
// Find all the paths that have changed as part of the difference and update the
|
||||||
// global dependency map if an existing dynamic binding has now become legal
|
// global dependency map if an existing dynamic binding has now become legal
|
||||||
const {
|
const {
|
||||||
dependenciesOfRemovedPaths,
|
dependenciesOfRemovedPaths,
|
||||||
removedPaths,
|
removedPaths,
|
||||||
} = this.updateDependencyMap(translatedDiffs, localUnEvalTree);
|
} = this.updateDependencyMap(translatedDiffs, localUnEvalTree);
|
||||||
|
|
||||||
const updateDependenciesStop = performance.now();
|
const updateDependenciesStop = performance.now();
|
||||||
|
|
||||||
this.applyDifferencesToEvalTree(differences);
|
this.applyDifferencesToEvalTree(differences);
|
||||||
|
|
@ -560,8 +563,8 @@ export default class DataTreeEvaluator {
|
||||||
}
|
}
|
||||||
if (isJSAction(entity)) {
|
if (isJSAction(entity)) {
|
||||||
// making functions dependent on their function body entities
|
// making functions dependent on their function body entities
|
||||||
if (entity.bindingPaths) {
|
if (entity.reactivePaths) {
|
||||||
Object.keys(entity.bindingPaths).forEach((propertyPath) => {
|
Object.keys(entity.reactivePaths).forEach((propertyPath) => {
|
||||||
const existingDeps =
|
const existingDeps =
|
||||||
dependencies[`${entityName}.${propertyPath}`] || [];
|
dependencies[`${entityName}.${propertyPath}`] || [];
|
||||||
const unevalPropValue = _.get(entity, propertyPath).toString();
|
const unevalPropValue = _.get(entity, propertyPath).toString();
|
||||||
|
|
@ -613,14 +616,14 @@ export default class DataTreeEvaluator {
|
||||||
fullPropertyPath,
|
fullPropertyPath,
|
||||||
);
|
);
|
||||||
|
|
||||||
const isABindingPath =
|
const isADynamicBindingPath =
|
||||||
(isAction(entity) || isWidget(entity) || isJSAction(entity)) &&
|
(isAction(entity) || isWidget(entity) || isJSAction(entity)) &&
|
||||||
isPathADynamicBinding(entity, propertyPath);
|
isPathADynamicBinding(entity, propertyPath);
|
||||||
const isATriggerPath =
|
const isATriggerPath =
|
||||||
isWidget(entity) && isPathADynamicTrigger(entity, propertyPath);
|
isWidget(entity) && isPathADynamicTrigger(entity, propertyPath);
|
||||||
let evalPropertyValue;
|
let evalPropertyValue;
|
||||||
const requiresEval =
|
const requiresEval =
|
||||||
isABindingPath &&
|
isADynamicBindingPath &&
|
||||||
!isATriggerPath &&
|
!isATriggerPath &&
|
||||||
(isDynamicValue(unEvalPropertyValue) || isJSAction(entity));
|
(isDynamicValue(unEvalPropertyValue) || isJSAction(entity));
|
||||||
if (propertyPath) {
|
if (propertyPath) {
|
||||||
|
|
@ -628,7 +631,7 @@ export default class DataTreeEvaluator {
|
||||||
}
|
}
|
||||||
if (requiresEval) {
|
if (requiresEval) {
|
||||||
const evaluationSubstitutionType =
|
const evaluationSubstitutionType =
|
||||||
entity.bindingPaths[propertyPath] ||
|
entity.reactivePaths[propertyPath] ||
|
||||||
EvaluationSubstitutionType.TEMPLATE;
|
EvaluationSubstitutionType.TEMPLATE;
|
||||||
|
|
||||||
const contextData: EvaluateContext = {};
|
const contextData: EvaluateContext = {};
|
||||||
|
|
@ -1330,14 +1333,12 @@ export default class DataTreeEvaluator {
|
||||||
| DataTreeWidget
|
| DataTreeWidget
|
||||||
| DataTreeJSAction;
|
| DataTreeJSAction;
|
||||||
const fullPropertyPath = dataTreeDiff.payload.propertyPath;
|
const fullPropertyPath = dataTreeDiff.payload.propertyPath;
|
||||||
const entityPropertyPath = fullPropertyPath.substring(
|
const entityPropertyPath = getPropertyPath(fullPropertyPath);
|
||||||
fullPropertyPath.indexOf(".") + 1,
|
const isADynamicBindingPath = isPathADynamicBinding(
|
||||||
);
|
|
||||||
const isABindingPath = isPathADynamicBinding(
|
|
||||||
entity,
|
entity,
|
||||||
entityPropertyPath,
|
entityPropertyPath,
|
||||||
);
|
);
|
||||||
if (isABindingPath) {
|
if (isADynamicBindingPath) {
|
||||||
didUpdateDependencyMap = true;
|
didUpdateDependencyMap = true;
|
||||||
|
|
||||||
const { jsSnippets } = getDynamicBindings(
|
const { jsSnippets } = getDynamicBindings(
|
||||||
|
|
@ -1381,11 +1382,12 @@ export default class DataTreeEvaluator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the whole binding was removed then the value
|
// If the whole binding was removed, then the value at this path would be a string without any bindings.
|
||||||
// at this path would be "".
|
// In this case, if the path exists in the dependency map and is a bindingPath, then remove it.
|
||||||
// In this case if the path exists in the dependency map
|
else if (
|
||||||
// remove it.
|
entity.bindingPaths[entityPropertyPath] &&
|
||||||
else if (fullPropertyPath in this.dependencyMap) {
|
fullPropertyPath in this.dependencyMap
|
||||||
|
) {
|
||||||
didUpdateDependencyMap = true;
|
didUpdateDependencyMap = true;
|
||||||
delete this.dependencyMap[fullPropertyPath];
|
delete this.dependencyMap[fullPropertyPath];
|
||||||
}
|
}
|
||||||
|
|
@ -1501,7 +1503,7 @@ export default class DataTreeEvaluator {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const parentPropertyPath = convertPathToString(d.path);
|
const parentPropertyPath = convertPathToString(d.path);
|
||||||
Object.keys(entity.bindingPaths).forEach((relativePath) => {
|
Object.keys(entity.reactivePaths).forEach((relativePath) => {
|
||||||
const childPropertyPath = `${entityName}.${relativePath}`;
|
const childPropertyPath = `${entityName}.${relativePath}`;
|
||||||
// Check if relative path has dynamic binding
|
// Check if relative path has dynamic binding
|
||||||
if (
|
if (
|
||||||
|
|
@ -1,10 +1,27 @@
|
||||||
import DataTreeEvaluator from "./DataTreeEvaluator";
|
import DataTreeEvaluator from "../DataTreeEvaluator";
|
||||||
|
import { unEvalTree } from "./mockUnEvalTree";
|
||||||
|
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||||
|
import { DataTreeDiff } from "workers/evaluationUtils";
|
||||||
|
import { ALL_WIDGETS_AND_CONFIG } from "utils/WidgetRegistry";
|
||||||
|
|
||||||
|
const widgetConfigMap = {};
|
||||||
|
ALL_WIDGETS_AND_CONFIG.map(([, config]) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: No types available
|
||||||
|
if (config.type && config.properties) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: No types available
|
||||||
|
widgetConfigMap[config.type] = {
|
||||||
|
defaultProperties: config.properties.default,
|
||||||
|
derivedProperties: config.properties.derived,
|
||||||
|
metaProperties: config.properties.meta,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const dataTreeEvaluator = new DataTreeEvaluator(widgetConfigMap);
|
||||||
|
|
||||||
describe("DataTreeEvaluator", () => {
|
describe("DataTreeEvaluator", () => {
|
||||||
let dataTreeEvaluator: DataTreeEvaluator;
|
|
||||||
beforeAll(() => {
|
|
||||||
dataTreeEvaluator = new DataTreeEvaluator({});
|
|
||||||
});
|
|
||||||
describe("evaluateActionBindings", () => {
|
describe("evaluateActionBindings", () => {
|
||||||
it("handles this.params.property", () => {
|
it("handles this.params.property", () => {
|
||||||
const result = dataTreeEvaluator.evaluateActionBindings(
|
const result = dataTreeEvaluator.evaluateActionBindings(
|
||||||
|
|
@ -106,4 +123,67 @@ describe("DataTreeEvaluator", () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("test updateDependencyMap", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: No types available
|
||||||
|
dataTreeEvaluator.createFirstTree(unEvalTree as DataTree);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initial dependencyMap computation", () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: No types available
|
||||||
|
dataTreeEvaluator.updateDataTree(unEvalTree as DataTree);
|
||||||
|
|
||||||
|
expect(dataTreeEvaluator.dependencyMap).toStrictEqual({
|
||||||
|
"Button2.text": ["Button1.text"],
|
||||||
|
Button2: ["Button2.text"],
|
||||||
|
Button1: ["Button1.text"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`When empty binding is modified from {{Button1.text}} to {{""}}`, () => {
|
||||||
|
const translatedDiffs = [
|
||||||
|
{
|
||||||
|
payload: {
|
||||||
|
propertyPath: "Button2.text",
|
||||||
|
value: '{{""}}',
|
||||||
|
},
|
||||||
|
event: "EDIT",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
dataTreeEvaluator.updateDependencyMap(
|
||||||
|
translatedDiffs as Array<DataTreeDiff>,
|
||||||
|
dataTreeEvaluator.oldUnEvalTree,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dataTreeEvaluator.dependencyMap).toStrictEqual({
|
||||||
|
"Button2.text": [],
|
||||||
|
Button2: ["Button2.text"],
|
||||||
|
Button1: ["Button1.text"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`When binding is removed`, () => {
|
||||||
|
const translatedDiffs = [
|
||||||
|
{
|
||||||
|
payload: {
|
||||||
|
propertyPath: "Button2.text",
|
||||||
|
value: "abc",
|
||||||
|
},
|
||||||
|
event: "EDIT",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
dataTreeEvaluator.updateDependencyMap(
|
||||||
|
translatedDiffs as Array<DataTreeDiff>,
|
||||||
|
dataTreeEvaluator.oldUnEvalTree,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dataTreeEvaluator.dependencyMap).toStrictEqual({
|
||||||
|
Button2: ["Button2.text"],
|
||||||
|
Button1: ["Button1.text"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
293
app/client/src/workers/DataTreeEvaluator/test/mockUnEvalTree.ts
Normal file
293
app/client/src/workers/DataTreeEvaluator/test/mockUnEvalTree.ts
Normal file
|
|
@ -0,0 +1,293 @@
|
||||||
|
export const unEvalTree = {
|
||||||
|
MainContainer: {
|
||||||
|
widgetName: "MainContainer",
|
||||||
|
backgroundColor: "none",
|
||||||
|
rightColumn: 2220,
|
||||||
|
snapColumns: 64,
|
||||||
|
detachFromLayout: true,
|
||||||
|
widgetId: "0",
|
||||||
|
topRow: 0,
|
||||||
|
bottomRow: 640,
|
||||||
|
containerStyle: "none",
|
||||||
|
snapRows: 113,
|
||||||
|
parentRowSpace: 1,
|
||||||
|
type: "CANVAS_WIDGET",
|
||||||
|
canExtend: true,
|
||||||
|
version: 52,
|
||||||
|
minHeight: 620,
|
||||||
|
parentColumnSpace: 1,
|
||||||
|
dynamicBindingPathList: [],
|
||||||
|
leftColumn: 0,
|
||||||
|
children: ["j9dpft2lpu", "l0yem4eh6l"],
|
||||||
|
defaultProps: {},
|
||||||
|
defaultMetaProps: [],
|
||||||
|
logBlackList: {},
|
||||||
|
meta: {},
|
||||||
|
propertyOverrideDependency: {},
|
||||||
|
overridingPropertyPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
|
triggerPaths: {},
|
||||||
|
validationPaths: {},
|
||||||
|
ENTITY_TYPE: "WIDGET",
|
||||||
|
privateWidgets: {},
|
||||||
|
},
|
||||||
|
Button1: {
|
||||||
|
widgetName: "Button1",
|
||||||
|
buttonColor: "#03B365",
|
||||||
|
displayName: "Button",
|
||||||
|
iconSVG: "/static/media/icon.cca02633.svg",
|
||||||
|
topRow: 15,
|
||||||
|
bottomRow: 19,
|
||||||
|
parentRowSpace: 10,
|
||||||
|
type: "BUTTON_WIDGET",
|
||||||
|
hideCard: false,
|
||||||
|
animateLoading: true,
|
||||||
|
parentColumnSpace: 26.421875,
|
||||||
|
dynamicTriggerPathList: [],
|
||||||
|
leftColumn: 20,
|
||||||
|
dynamicBindingPathList: [],
|
||||||
|
text: "button1",
|
||||||
|
isDisabled: false,
|
||||||
|
key: "r6h8y6dc8i",
|
||||||
|
rightColumn: 36,
|
||||||
|
isDefaultClickDisabled: true,
|
||||||
|
widgetId: "j9dpft2lpu",
|
||||||
|
isVisible: true,
|
||||||
|
recaptchaType: "V3",
|
||||||
|
version: 1,
|
||||||
|
parentId: "0",
|
||||||
|
renderMode: "CANVAS",
|
||||||
|
isLoading: false,
|
||||||
|
buttonVariant: "PRIMARY",
|
||||||
|
placement: "CENTER",
|
||||||
|
defaultProps: {},
|
||||||
|
defaultMetaProps: ["recaptchaToken"],
|
||||||
|
logBlackList: {},
|
||||||
|
meta: {},
|
||||||
|
propertyOverrideDependency: {},
|
||||||
|
overridingPropertyPaths: {},
|
||||||
|
reactivePaths: {
|
||||||
|
recaptchaToken: "TEMPLATE",
|
||||||
|
text: "TEMPLATE",
|
||||||
|
tooltip: "TEMPLATE",
|
||||||
|
googleRecaptchaKey: "TEMPLATE",
|
||||||
|
recaptchaType: "TEMPLATE",
|
||||||
|
isVisible: "TEMPLATE",
|
||||||
|
isDisabled: "TEMPLATE",
|
||||||
|
animateLoading: "TEMPLATE",
|
||||||
|
buttonVariant: "TEMPLATE",
|
||||||
|
placement: "TEMPLATE",
|
||||||
|
},
|
||||||
|
triggerPaths: {
|
||||||
|
onClick: true,
|
||||||
|
},
|
||||||
|
validationPaths: {
|
||||||
|
text: {
|
||||||
|
type: "TEXT",
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
type: "TEXT",
|
||||||
|
},
|
||||||
|
googleRecaptchaKey: {
|
||||||
|
type: "TEXT",
|
||||||
|
},
|
||||||
|
recaptchaType: {
|
||||||
|
type: "TEXT",
|
||||||
|
params: {
|
||||||
|
allowedValues: ["V3", "V2"],
|
||||||
|
default: "V3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isVisible: {
|
||||||
|
type: "BOOLEAN",
|
||||||
|
},
|
||||||
|
isDisabled: {
|
||||||
|
type: "BOOLEAN",
|
||||||
|
},
|
||||||
|
animateLoading: {
|
||||||
|
type: "BOOLEAN",
|
||||||
|
},
|
||||||
|
buttonVariant: {
|
||||||
|
type: "TEXT",
|
||||||
|
params: {
|
||||||
|
allowedValues: ["PRIMARY", "SECONDARY", "TERTIARY"],
|
||||||
|
default: "PRIMARY",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
placement: {
|
||||||
|
type: "TEXT",
|
||||||
|
params: {
|
||||||
|
allowedValues: ["START", "BETWEEN", "CENTER"],
|
||||||
|
default: "CENTER",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ENTITY_TYPE: "WIDGET",
|
||||||
|
privateWidgets: {},
|
||||||
|
},
|
||||||
|
Button2: {
|
||||||
|
widgetName: "Button2",
|
||||||
|
buttonColor: "#03B365",
|
||||||
|
displayName: "Button",
|
||||||
|
iconSVG: "/static/media/icon.cca02633.svg",
|
||||||
|
topRow: 25,
|
||||||
|
bottomRow: 29,
|
||||||
|
parentRowSpace: 10,
|
||||||
|
type: "BUTTON_WIDGET",
|
||||||
|
hideCard: false,
|
||||||
|
animateLoading: true,
|
||||||
|
parentColumnSpace: 26.421875,
|
||||||
|
dynamicTriggerPathList: [],
|
||||||
|
leftColumn: 20,
|
||||||
|
dynamicBindingPathList: [
|
||||||
|
{
|
||||||
|
key: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
text: "{{Button1.text}}",
|
||||||
|
isDisabled: false,
|
||||||
|
key: "r6h8y6dc8i",
|
||||||
|
rightColumn: 36,
|
||||||
|
isDefaultClickDisabled: true,
|
||||||
|
widgetId: "l0yem4eh6l",
|
||||||
|
isVisible: true,
|
||||||
|
recaptchaType: "V3",
|
||||||
|
version: 1,
|
||||||
|
parentId: "0",
|
||||||
|
renderMode: "CANVAS",
|
||||||
|
isLoading: false,
|
||||||
|
buttonVariant: "PRIMARY",
|
||||||
|
placement: "CENTER",
|
||||||
|
defaultProps: {},
|
||||||
|
defaultMetaProps: ["recaptchaToken"],
|
||||||
|
logBlackList: {},
|
||||||
|
meta: {},
|
||||||
|
propertyOverrideDependency: {},
|
||||||
|
overridingPropertyPaths: {},
|
||||||
|
reactivePaths: {
|
||||||
|
recaptchaToken: "TEMPLATE",
|
||||||
|
text: "TEMPLATE",
|
||||||
|
tooltip: "TEMPLATE",
|
||||||
|
googleRecaptchaKey: "TEMPLATE",
|
||||||
|
recaptchaType: "TEMPLATE",
|
||||||
|
isVisible: "TEMPLATE",
|
||||||
|
isDisabled: "TEMPLATE",
|
||||||
|
animateLoading: "TEMPLATE",
|
||||||
|
buttonVariant: "TEMPLATE",
|
||||||
|
placement: "TEMPLATE",
|
||||||
|
},
|
||||||
|
triggerPaths: {
|
||||||
|
onClick: true,
|
||||||
|
},
|
||||||
|
validationPaths: {
|
||||||
|
text: {
|
||||||
|
type: "TEXT",
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
type: "TEXT",
|
||||||
|
},
|
||||||
|
googleRecaptchaKey: {
|
||||||
|
type: "TEXT",
|
||||||
|
},
|
||||||
|
recaptchaType: {
|
||||||
|
type: "TEXT",
|
||||||
|
params: {
|
||||||
|
allowedValues: ["V3", "V2"],
|
||||||
|
default: "V3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isVisible: {
|
||||||
|
type: "BOOLEAN",
|
||||||
|
},
|
||||||
|
isDisabled: {
|
||||||
|
type: "BOOLEAN",
|
||||||
|
},
|
||||||
|
animateLoading: {
|
||||||
|
type: "BOOLEAN",
|
||||||
|
},
|
||||||
|
buttonVariant: {
|
||||||
|
type: "TEXT",
|
||||||
|
params: {
|
||||||
|
allowedValues: ["PRIMARY", "SECONDARY", "TERTIARY"],
|
||||||
|
default: "PRIMARY",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
placement: {
|
||||||
|
type: "TEXT",
|
||||||
|
params: {
|
||||||
|
allowedValues: ["START", "BETWEEN", "CENTER"],
|
||||||
|
default: "CENTER",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ENTITY_TYPE: "WIDGET",
|
||||||
|
privateWidgets: {},
|
||||||
|
},
|
||||||
|
pageList: [
|
||||||
|
{
|
||||||
|
pageName: "Page1",
|
||||||
|
pageId: "6200d1a2b5bfc0392b959cae",
|
||||||
|
isDefault: true,
|
||||||
|
isHidden: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pageName: "Page2",
|
||||||
|
pageId: "621e22cf2b75295c1c165fa6",
|
||||||
|
isDefault: false,
|
||||||
|
isHidden: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pageName: "Page3",
|
||||||
|
pageId: "6220c268c48234070f8ac65a",
|
||||||
|
isDefault: false,
|
||||||
|
isHidden: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
appsmith: {
|
||||||
|
user: {
|
||||||
|
email: "rathod@appsmith.com",
|
||||||
|
organizationIds: [
|
||||||
|
"6218a61972ccd9145ec78c57",
|
||||||
|
"621913df0276eb01d22fec44",
|
||||||
|
"60caf8edb1e47a1315f0c48f",
|
||||||
|
"609114fe05c4d35a9f6cbbf2",
|
||||||
|
],
|
||||||
|
username: "rathod@appsmith.com",
|
||||||
|
name: "Rishabh",
|
||||||
|
commentOnboardingState: "RESOLVED",
|
||||||
|
role: "engineer",
|
||||||
|
useCase: "personal project",
|
||||||
|
enableTelemetry: false,
|
||||||
|
emptyInstance: false,
|
||||||
|
accountNonExpired: true,
|
||||||
|
accountNonLocked: true,
|
||||||
|
credentialsNonExpired: true,
|
||||||
|
isAnonymous: false,
|
||||||
|
isEnabled: true,
|
||||||
|
isSuperUser: false,
|
||||||
|
isConfigurable: true,
|
||||||
|
},
|
||||||
|
URL: {
|
||||||
|
fullPath:
|
||||||
|
"https://dev.appsmith.com/applications/6200d1a2b5bfc0392b959cab/pages/6220c268c48234070f8ac65a/edit?a=b",
|
||||||
|
host: "dev.appsmith.com",
|
||||||
|
hostname: "dev.appsmith.com",
|
||||||
|
queryParams: {
|
||||||
|
a: "b",
|
||||||
|
},
|
||||||
|
protocol: "https:",
|
||||||
|
pathname:
|
||||||
|
"/applications/6200d1a2b5bfc0392b959cab/pages/6220c268c48234070f8ac65a/edit",
|
||||||
|
port: "",
|
||||||
|
hash: "",
|
||||||
|
},
|
||||||
|
store: {
|
||||||
|
textColor: "#DF7E65",
|
||||||
|
},
|
||||||
|
geolocation: {
|
||||||
|
canBeRequested: true,
|
||||||
|
},
|
||||||
|
mode: "EDIT",
|
||||||
|
ENTITY_TYPE: "APPSMITH",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -27,6 +27,7 @@ describe("evaluateSync", () => {
|
||||||
text: "value",
|
text: "value",
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
triggerPaths: {},
|
triggerPaths: {},
|
||||||
validationPaths: {},
|
validationPaths: {},
|
||||||
logBlackList: {},
|
logBlackList: {},
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,11 @@ import {
|
||||||
import { WidgetTypeConfigMap } from "utils/WidgetFactory";
|
import { WidgetTypeConfigMap } from "utils/WidgetFactory";
|
||||||
import { RenderModes } from "constants/WidgetConstants";
|
import { RenderModes } from "constants/WidgetConstants";
|
||||||
import { PluginType } from "entities/Action";
|
import { PluginType } from "entities/Action";
|
||||||
import DataTreeEvaluator from "workers/DataTreeEvaluator";
|
import DataTreeEvaluator from "workers/DataTreeEvaluator/DataTreeEvaluator";
|
||||||
import { ValidationTypes } from "constants/WidgetValidation";
|
import { ValidationTypes } from "constants/WidgetValidation";
|
||||||
import WidgetFactory from "utils/WidgetFactory";
|
import WidgetFactory from "utils/WidgetFactory";
|
||||||
import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget";
|
import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget";
|
||||||
|
import { sortObjectWithArray } from "../utils/treeUtils";
|
||||||
/**
|
|
||||||
* This function sorts the object's value which is array of string.
|
|
||||||
*
|
|
||||||
* @param {Record<string, Array<string>>} data
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const sortObject = (data: Record<string, Array<string>>) => {
|
|
||||||
Object.entries(data).map(([key, value]) => {
|
|
||||||
data[key] = value.sort();
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
const WIDGET_CONFIG_MAP: WidgetTypeConfigMap = {
|
const WIDGET_CONFIG_MAP: WidgetTypeConfigMap = {
|
||||||
CONTAINER_WIDGET: {
|
CONTAINER_WIDGET: {
|
||||||
|
|
@ -230,6 +218,7 @@ const BASE_WIDGET: DataTreeWidget = {
|
||||||
parentId: "0",
|
parentId: "0",
|
||||||
version: 1,
|
version: 1,
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
triggerPaths: {},
|
triggerPaths: {},
|
||||||
validationPaths: {},
|
validationPaths: {},
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||||
|
|
@ -254,7 +243,8 @@ const BASE_ACTION: DataTreeAction = {
|
||||||
data: {},
|
data: {},
|
||||||
responseMeta: { isExecutionSuccess: false },
|
responseMeta: { isExecutionSuccess: false },
|
||||||
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||||
bindingPaths: {
|
bindingPaths: {},
|
||||||
|
reactivePaths: {
|
||||||
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
isLoading: EvaluationSubstitutionType.TEMPLATE,
|
||||||
data: EvaluationSubstitutionType.TEMPLATE,
|
data: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
|
|
@ -334,7 +324,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
defaultText: "Default value",
|
defaultText: "Default value",
|
||||||
widgetName: "Input1",
|
widgetName: "Input1",
|
||||||
type: "INPUT_WIDGET_V2",
|
type: "INPUT_WIDGET_V2",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
defaultText: EvaluationSubstitutionType.TEMPLATE,
|
defaultText: EvaluationSubstitutionType.TEMPLATE,
|
||||||
isValid: EvaluationSubstitutionType.TEMPLATE,
|
isValid: EvaluationSubstitutionType.TEMPLATE,
|
||||||
value: EvaluationSubstitutionType.TEMPLATE,
|
value: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -406,7 +396,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
text: "{{Table1.selectedRow.test}}",
|
text: "{{Table1.selectedRow.test}}",
|
||||||
dynamicBindingPathList: [{ key: "text" }],
|
dynamicBindingPathList: [{ key: "text" }],
|
||||||
type: "TEXT_WIDGET",
|
type: "TEXT_WIDGET",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
validationPaths: {
|
validationPaths: {
|
||||||
|
|
@ -424,7 +414,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
|
|
||||||
expect(evaluation).toHaveProperty("Text2.text", "Label");
|
expect(evaluation).toHaveProperty("Text2.text", "Label");
|
||||||
expect(evaluation).toHaveProperty("Text3.text", "Label");
|
expect(evaluation).toHaveProperty("Text3.text", "Label");
|
||||||
expect(sortObject(dependencyMap)).toStrictEqual(dependencyMap);
|
expect(sortObjectWithArray(dependencyMap)).toStrictEqual(dependencyMap);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Evaluates a value change in update run", () => {
|
it("Evaluates a value change in update run", () => {
|
||||||
|
|
@ -455,7 +445,9 @@ describe("DataTreeEvaluator", () => {
|
||||||
expect(dataTree).toHaveProperty("Text2.text", "Label");
|
expect(dataTree).toHaveProperty("Text2.text", "Label");
|
||||||
expect(dataTree).toHaveProperty("Text3.text", "Label 3");
|
expect(dataTree).toHaveProperty("Text3.text", "Label 3");
|
||||||
|
|
||||||
expect(sortObject(updatedDependencyMap)).toStrictEqual(dependencyMap);
|
expect(sortObjectWithArray(updatedDependencyMap)).toStrictEqual(
|
||||||
|
dependencyMap,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Overrides with default value", () => {
|
it("Overrides with default value", () => {
|
||||||
|
|
@ -470,6 +462,13 @@ describe("DataTreeEvaluator", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Evaluates for value changes in nested diff paths", () => {
|
it("Evaluates for value changes in nested diff paths", () => {
|
||||||
|
const bindingPaths = {
|
||||||
|
options: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
defaultOptionValue: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isRequired: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isVisible: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
isDisabled: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
};
|
||||||
const updatedUnEvalTree = {
|
const updatedUnEvalTree = {
|
||||||
...unEvalTree,
|
...unEvalTree,
|
||||||
Dropdown2: {
|
Dropdown2: {
|
||||||
|
|
@ -485,12 +484,9 @@ describe("DataTreeEvaluator", () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
type: "SELECT_WIDGET",
|
type: "SELECT_WIDGET",
|
||||||
bindingPaths: {
|
bindingPaths,
|
||||||
options: EvaluationSubstitutionType.TEMPLATE,
|
reactivePaths: {
|
||||||
defaultOptionValue: EvaluationSubstitutionType.TEMPLATE,
|
...bindingPaths,
|
||||||
isRequired: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isVisible: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isDisabled: EvaluationSubstitutionType.TEMPLATE,
|
|
||||||
isValid: EvaluationSubstitutionType.TEMPLATE,
|
isValid: EvaluationSubstitutionType.TEMPLATE,
|
||||||
selectedOption: EvaluationSubstitutionType.TEMPLATE,
|
selectedOption: EvaluationSubstitutionType.TEMPLATE,
|
||||||
selectedOptionValue: EvaluationSubstitutionType.TEMPLATE,
|
selectedOptionValue: EvaluationSubstitutionType.TEMPLATE,
|
||||||
|
|
@ -533,7 +529,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(sortObject(updatedDependencyMap)).toStrictEqual({
|
expect(sortObjectWithArray(updatedDependencyMap)).toStrictEqual({
|
||||||
Api1: ["Api1.data"],
|
Api1: ["Api1.data"],
|
||||||
...dependencyMap,
|
...dependencyMap,
|
||||||
"Table1.tableData": ["Api1.data", "Text1.text"],
|
"Table1.tableData": ["Api1.data", "Text1.text"],
|
||||||
|
|
@ -579,7 +575,7 @@ describe("DataTreeEvaluator", () => {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(dataTree).toHaveProperty("Text4.text", "Hey");
|
expect(dataTree).toHaveProperty("Text4.text", "Hey");
|
||||||
expect(sortObject(updatedDependencyMap)).toStrictEqual({
|
expect(sortObjectWithArray(updatedDependencyMap)).toStrictEqual({
|
||||||
Api1: ["Api1.data"],
|
Api1: ["Api1.data"],
|
||||||
...dependencyMap,
|
...dependencyMap,
|
||||||
"Table1.tableData": ["Api1.data", "Text1.text"],
|
"Table1.tableData": ["Api1.data", "Text1.text"],
|
||||||
|
|
@ -599,8 +595,8 @@ describe("DataTreeEvaluator", () => {
|
||||||
dependencyMap: {
|
dependencyMap: {
|
||||||
"config.body": ["config.pluginSpecifiedTemplates[0].value"],
|
"config.body": ["config.pluginSpecifiedTemplates[0].value"],
|
||||||
},
|
},
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
...BASE_ACTION.bindingPaths,
|
...BASE_ACTION.reactivePaths,
|
||||||
"config.body": EvaluationSubstitutionType.TEMPLATE,
|
"config.body": EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
|
|
@ -646,8 +642,8 @@ describe("DataTreeEvaluator", () => {
|
||||||
...updatedTree2,
|
...updatedTree2,
|
||||||
Api2: {
|
Api2: {
|
||||||
...updatedTree2.Api2,
|
...updatedTree2.Api2,
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
...updatedTree2.Api2.bindingPaths,
|
...updatedTree2.Api2.reactivePaths,
|
||||||
"config.body": EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
"config.body": EvaluationSubstitutionType.SMART_SUBSTITUTE,
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import {
|
||||||
removeFunctions,
|
removeFunctions,
|
||||||
validateWidgetProperty,
|
validateWidgetProperty,
|
||||||
} from "./evaluationUtils";
|
} from "./evaluationUtils";
|
||||||
import DataTreeEvaluator from "workers/DataTreeEvaluator";
|
import DataTreeEvaluator from "workers/DataTreeEvaluator/DataTreeEvaluator";
|
||||||
import ReplayEntity from "entities/Replay";
|
import ReplayEntity from "entities/Replay";
|
||||||
import evaluate, {
|
import evaluate, {
|
||||||
evaluateAsync,
|
evaluateAsync,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ const BASE_WIDGET: DataTreeWidget = {
|
||||||
parentId: "0",
|
parentId: "0",
|
||||||
version: 1,
|
version: 1,
|
||||||
bindingPaths: {},
|
bindingPaths: {},
|
||||||
|
reactivePaths: {},
|
||||||
triggerPaths: {},
|
triggerPaths: {},
|
||||||
validationPaths: {},
|
validationPaths: {},
|
||||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||||
|
|
@ -55,7 +56,7 @@ const testDataTree: Record<string, DataTreeWidget> = {
|
||||||
widgetName: "Text1",
|
widgetName: "Text1",
|
||||||
text: "Label",
|
text: "Label",
|
||||||
type: "TEXT_WIDGET",
|
type: "TEXT_WIDGET",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
validationPaths: {
|
validationPaths: {
|
||||||
|
|
@ -68,7 +69,7 @@ const testDataTree: Record<string, DataTreeWidget> = {
|
||||||
text: "{{Text1.text}}",
|
text: "{{Text1.text}}",
|
||||||
dynamicBindingPathList: [{ key: "text" }],
|
dynamicBindingPathList: [{ key: "text" }],
|
||||||
type: "TEXT_WIDGET",
|
type: "TEXT_WIDGET",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
validationPaths: {
|
validationPaths: {
|
||||||
|
|
@ -81,7 +82,7 @@ const testDataTree: Record<string, DataTreeWidget> = {
|
||||||
text: "{{Text1.text}}",
|
text: "{{Text1.text}}",
|
||||||
dynamicBindingPathList: [{ key: "text" }],
|
dynamicBindingPathList: [{ key: "text" }],
|
||||||
type: "TEXT_WIDGET",
|
type: "TEXT_WIDGET",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
validationPaths: {
|
validationPaths: {
|
||||||
|
|
@ -94,7 +95,7 @@ const testDataTree: Record<string, DataTreeWidget> = {
|
||||||
text: "{{Text1.text}}",
|
text: "{{Text1.text}}",
|
||||||
dynamicBindingPathList: [{ key: "text" }],
|
dynamicBindingPathList: [{ key: "text" }],
|
||||||
type: "TEXT_WIDGET",
|
type: "TEXT_WIDGET",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
validationPaths: {
|
validationPaths: {
|
||||||
|
|
@ -198,7 +199,7 @@ describe("privateWidgets", () => {
|
||||||
widgetName: "Text1",
|
widgetName: "Text1",
|
||||||
text: "Label",
|
text: "Label",
|
||||||
type: "TEXT_WIDGET",
|
type: "TEXT_WIDGET",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
validationPaths: {
|
validationPaths: {
|
||||||
|
|
@ -212,7 +213,7 @@ describe("privateWidgets", () => {
|
||||||
text: "{{Text1.text}}",
|
text: "{{Text1.text}}",
|
||||||
dynamicBindingPathList: [{ key: "text" }],
|
dynamicBindingPathList: [{ key: "text" }],
|
||||||
type: "TEXT_WIDGET",
|
type: "TEXT_WIDGET",
|
||||||
bindingPaths: {
|
reactivePaths: {
|
||||||
text: EvaluationSubstitutionType.TEMPLATE,
|
text: EvaluationSubstitutionType.TEMPLATE,
|
||||||
},
|
},
|
||||||
validationPaths: {
|
validationPaths: {
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ export function getEntityNameAndPropertyPath(
|
||||||
|
|
||||||
//these paths are not required to go through evaluate tree as these are internal properties
|
//these paths are not required to go through evaluate tree as these are internal properties
|
||||||
const ignorePathsForEvalRegex =
|
const ignorePathsForEvalRegex =
|
||||||
".(bindingPaths|triggerPaths|validationPaths|dynamicBindingPathList)";
|
".(reactivePaths|bindingPaths|triggerPaths|validationPaths|dynamicBindingPathList)";
|
||||||
|
|
||||||
//match if paths are part of ignorePathsForEvalRegex
|
//match if paths are part of ignorePathsForEvalRegex
|
||||||
const isUninterestingChangeForDependencyUpdate = (path: string) => {
|
const isUninterestingChangeForDependencyUpdate = (path: string) => {
|
||||||
|
|
@ -602,7 +602,7 @@ export const isDynamicLeaf = (unEvalTree: DataTree, propertyPath: string) => {
|
||||||
return false;
|
return false;
|
||||||
const relativePropertyPath = convertPathToString(propPathEls);
|
const relativePropertyPath = convertPathToString(propPathEls);
|
||||||
return (
|
return (
|
||||||
relativePropertyPath in entity.bindingPaths ||
|
relativePropertyPath in entity.reactivePaths ||
|
||||||
(isWidget(entity) && relativePropertyPath in entity.triggerPaths)
|
(isWidget(entity) && relativePropertyPath in entity.triggerPaths)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -642,14 +642,15 @@ export const updateJSCollectionInDataTree = (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const bindingPaths = jsCollection.bindingPaths;
|
const reactivePaths = jsCollection.reactivePaths;
|
||||||
bindingPaths[action.name] = EvaluationSubstitutionType.SMART_SUBSTITUTE;
|
reactivePaths[action.name] =
|
||||||
bindingPaths[`${action.name}.data`] =
|
EvaluationSubstitutionType.SMART_SUBSTITUTE;
|
||||||
|
reactivePaths[`${action.name}.data`] =
|
||||||
EvaluationSubstitutionType.TEMPLATE;
|
EvaluationSubstitutionType.TEMPLATE;
|
||||||
_.set(
|
_.set(
|
||||||
modifiedDataTree,
|
modifiedDataTree,
|
||||||
`${jsCollection.name}.bindingPaths`,
|
`${jsCollection.name}.reactivePaths`,
|
||||||
bindingPaths,
|
reactivePaths,
|
||||||
);
|
);
|
||||||
const dynamicBindingPathList = jsCollection.dynamicBindingPathList;
|
const dynamicBindingPathList = jsCollection.dynamicBindingPathList;
|
||||||
dynamicBindingPathList.push({ key: action.name });
|
dynamicBindingPathList.push({ key: action.name });
|
||||||
|
|
@ -697,12 +698,12 @@ export const updateJSCollectionInDataTree = (
|
||||||
(js: ParsedJSSubAction) => js.name === preAction,
|
(js: ParsedJSSubAction) => js.name === preAction,
|
||||||
);
|
);
|
||||||
if (!existed) {
|
if (!existed) {
|
||||||
const bindingPaths = jsCollection.bindingPaths;
|
const reactivePaths = jsCollection.reactivePaths;
|
||||||
delete bindingPaths[preAction];
|
delete reactivePaths[preAction];
|
||||||
_.set(
|
_.set(
|
||||||
modifiedDataTree,
|
modifiedDataTree,
|
||||||
`${jsCollection.name}.bindingPaths`,
|
`${jsCollection.name}.reactivePaths`,
|
||||||
bindingPaths,
|
reactivePaths,
|
||||||
);
|
);
|
||||||
let dynamicBindingPathList = jsCollection.dynamicBindingPathList;
|
let dynamicBindingPathList = jsCollection.dynamicBindingPathList;
|
||||||
dynamicBindingPathList = dynamicBindingPathList.filter(
|
dynamicBindingPathList = dynamicBindingPathList.filter(
|
||||||
|
|
@ -796,12 +797,12 @@ export const removeFunctionsAndVariableJSCollection = (
|
||||||
}
|
}
|
||||||
//remove functions
|
//remove functions
|
||||||
let dynamicBindingPathList = entity.dynamicBindingPathList;
|
let dynamicBindingPathList = entity.dynamicBindingPathList;
|
||||||
const bindingPaths = entity.bindingPaths;
|
const reactivePaths = entity.reactivePaths;
|
||||||
const meta = entity.meta;
|
const meta = entity.meta;
|
||||||
let dependencyMap = entity.dependencyMap["body"];
|
let dependencyMap = entity.dependencyMap["body"];
|
||||||
for (let i = 0; i < functionsList.length; i++) {
|
for (let i = 0; i < functionsList.length; i++) {
|
||||||
const actionName = functionsList[i];
|
const actionName = functionsList[i];
|
||||||
delete bindingPaths[actionName];
|
delete reactivePaths[actionName];
|
||||||
delete meta[actionName];
|
delete meta[actionName];
|
||||||
delete modifiedDataTree[`${entity.name}`][`${actionName}`];
|
delete modifiedDataTree[`${entity.name}`][`${actionName}`];
|
||||||
dynamicBindingPathList = dynamicBindingPathList.filter(
|
dynamicBindingPathList = dynamicBindingPathList.filter(
|
||||||
|
|
@ -809,7 +810,7 @@ export const removeFunctionsAndVariableJSCollection = (
|
||||||
);
|
);
|
||||||
dependencyMap = dependencyMap.filter((item: any) => item !== actionName);
|
dependencyMap = dependencyMap.filter((item: any) => item !== actionName);
|
||||||
}
|
}
|
||||||
_.set(modifiedDataTree, `${entity.name}.bindingPaths`, bindingPaths);
|
_.set(modifiedDataTree, `${entity.name}.reactivePaths`, reactivePaths);
|
||||||
_.set(
|
_.set(
|
||||||
modifiedDataTree,
|
modifiedDataTree,
|
||||||
`${entity.name}.dynamicBindingPathList`,
|
`${entity.name}.dynamicBindingPathList`,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user