Add back derived properties
This commit is contained in:
parent
17b642d085
commit
cc50beb0a0
|
|
@ -82,6 +82,7 @@
|
||||||
"source-map-explorer": "^2.1.1",
|
"source-map-explorer": "^2.1.1",
|
||||||
"styled-components": "^4.1.3",
|
"styled-components": "^4.1.3",
|
||||||
"tinycolor2": "^1.4.1",
|
"tinycolor2": "^1.4.1",
|
||||||
|
"toposort": "^2.0.2",
|
||||||
"ts-loader": "^6.0.4",
|
"ts-loader": "^6.0.4",
|
||||||
"typescript": "^3.6.3",
|
"typescript": "^3.6.3",
|
||||||
"unescape-js": "^1.1.4"
|
"unescape-js": "^1.1.4"
|
||||||
|
|
@ -123,6 +124,7 @@
|
||||||
"@types/react-select": "^3.0.5",
|
"@types/react-select": "^3.0.5",
|
||||||
"@types/react-tabs": "^2.3.1",
|
"@types/react-tabs": "^2.3.1",
|
||||||
"@types/redux-form": "^8.1.9",
|
"@types/redux-form": "^8.1.9",
|
||||||
|
"@types/toposort": "^2.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.0.0",
|
"@typescript-eslint/eslint-plugin": "^2.0.0",
|
||||||
"@typescript-eslint/parser": "^2.0.0",
|
"@typescript-eslint/parser": "^2.0.0",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable no-useless-escape */
|
/* eslint-disable no-useless-escape */
|
||||||
// TODO (hetu): Remove useless escapes and re-enable the above lint rule
|
// TODO (hetu): Remove useless escapes and re-enable the above lint rule
|
||||||
export type NamePathBindingMap = Record<string, string>;
|
export type NamePathBindingMap = Record<string, string>;
|
||||||
export const DATA_BIND_REGEX = /(.*?){{(\s*(.*?)\s*)}}(.*?)/g;
|
export const DATA_BIND_REGEX = /{{([\s\S]*?)}}/g;
|
||||||
export const AUTOCOMPLETE_MATCH_REGEX = /{{\s*.*?\s*}}/g;
|
export const AUTOCOMPLETE_MATCH_REGEX = /{{\s*.*?\s*}}/g;
|
||||||
/* eslint-enable no-useless-escape */
|
/* eslint-enable no-useless-escape */
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,14 @@ export default class RealmExecutor implements JSExecutor {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.rootRealm = Realm.makeRootRealm();
|
this.rootRealm = Realm.makeRootRealm();
|
||||||
this.createSafeFunction = this.rootRealm.evaluate(`
|
this.createSafeFunction = this.rootRealm.evaluate(`
|
||||||
(function createSafeFunction(unsafeFn) {
|
(function createSafeFunction(unsafeFn) {
|
||||||
return function safeFn(...args) {
|
return function safeFn(...args) {
|
||||||
unsafeFn(...args);
|
unsafeFn(...args);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
`);
|
`);
|
||||||
this.createSafeObject = this.rootRealm.evaluate(`
|
this.createSafeObject = this.rootRealm.evaluate(`
|
||||||
(function creaetSafeObject(unsafeObject) {
|
(function creaetSafeObject(unsafeObject) {
|
||||||
return JSON.parse(JSON.stringify(unsafeObject));
|
return JSON.parse(JSON.stringify(unsafeObject));
|
||||||
})
|
})
|
||||||
`);
|
`);
|
||||||
|
|
@ -44,7 +44,7 @@ export default class RealmExecutor implements JSExecutor {
|
||||||
try {
|
try {
|
||||||
result = this.rootRealm.evaluate(sourceText, safeData);
|
result = this.rootRealm.evaluate(sourceText, safeData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//TODO(Satbir): Return an object with an error message.
|
console.error(`Error: "${e.message}" when evaluating {{${sourceText}}}`);
|
||||||
}
|
}
|
||||||
return this.convertToMainScope(result);
|
return this.convertToMainScope(result);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,10 +52,7 @@ import { getFormData } from "selectors/formSelectors";
|
||||||
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
||||||
import { executeAction } from "actions/widgetActions";
|
import { executeAction } from "actions/widgetActions";
|
||||||
import JSExecutionManagerSingleton from "jsExecution/JSExecutionManagerSingleton";
|
import JSExecutionManagerSingleton from "jsExecution/JSExecutionManagerSingleton";
|
||||||
import {
|
import { getParsedDataTree } from "selectors/nameBindingsWithDataSelector";
|
||||||
getNameBindingsWithData,
|
|
||||||
NameBindingsWithData,
|
|
||||||
} from "selectors/nameBindingsWithDataSelector";
|
|
||||||
import { transformRestAction } from "transformers/RestActionTransformer";
|
import { transformRestAction } from "transformers/RestActionTransformer";
|
||||||
|
|
||||||
export const getAction = (
|
export const getAction = (
|
||||||
|
|
@ -83,8 +80,8 @@ const createActionErrorResponse = (
|
||||||
});
|
});
|
||||||
|
|
||||||
export function* evaluateDynamicBoundValueSaga(path: string): any {
|
export function* evaluateDynamicBoundValueSaga(path: string): any {
|
||||||
const nameBindingsWithData = yield select(getNameBindingsWithData);
|
const tree = yield select(getParsedDataTree);
|
||||||
return getDynamicValue(`{{${path}}}`, nameBindingsWithData);
|
return getDynamicValue(`{{${path}}}`, tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* getActionParams(jsonPathKeys: string[] | undefined) {
|
export function* getActionParams(jsonPathKeys: string[] | undefined) {
|
||||||
|
|
@ -106,12 +103,10 @@ export function* getActionParams(jsonPathKeys: string[] | undefined) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function* executeJSActionSaga(jsAction: ExecuteJSActionPayload) {
|
function* executeJSActionSaga(jsAction: ExecuteJSActionPayload) {
|
||||||
const nameBindingsWithData: NameBindingsWithData = yield select(
|
const tree = yield select(getParsedDataTree);
|
||||||
getNameBindingsWithData,
|
|
||||||
);
|
|
||||||
const result = JSExecutionManagerSingleton.evaluateSync(
|
const result = JSExecutionManagerSingleton.evaluateSync(
|
||||||
jsAction.jsFunction,
|
jsAction.jsFunction,
|
||||||
nameBindingsWithData,
|
tree,
|
||||||
);
|
);
|
||||||
|
|
||||||
yield put({
|
yield put({
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { WidgetConfigReducerState } from "reducers/entityReducers/widgetConfigRe
|
||||||
import { WidgetCardProps } from "widgets/BaseWidget";
|
import { WidgetCardProps } from "widgets/BaseWidget";
|
||||||
import { WidgetSidebarReduxState } from "reducers/uiReducers/widgetSidebarReducer";
|
import { WidgetSidebarReduxState } from "reducers/uiReducers/widgetSidebarReducer";
|
||||||
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
|
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
|
||||||
import { enhanceWithDynamicValuesAndValidations } from "utils/DynamicBindingUtils";
|
import { getEvaluatedDataTree, getParsedTree } from "utils/DynamicBindingUtils";
|
||||||
import { getDataTree } from "./entitiesSelector";
|
import { getDataTree } from "./entitiesSelector";
|
||||||
import {
|
import {
|
||||||
FlattenedWidgetProps,
|
FlattenedWidgetProps,
|
||||||
|
|
@ -18,9 +18,11 @@ import { PageListReduxState } from "reducers/entityReducers/pageListReducer";
|
||||||
import { OccupiedSpace } from "constants/editorConstants";
|
import { OccupiedSpace } from "constants/editorConstants";
|
||||||
import { WidgetTypes } from "constants/WidgetConstants";
|
import { WidgetTypes } from "constants/WidgetConstants";
|
||||||
import {
|
import {
|
||||||
getNameBindingsWithData,
|
|
||||||
NameBindingsWithData,
|
NameBindingsWithData,
|
||||||
|
getNameBindingsWithData,
|
||||||
|
getParsedDataTree,
|
||||||
} from "./nameBindingsWithDataSelector";
|
} from "./nameBindingsWithDataSelector";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
const getEditorState = (state: AppState) => state.ui.editor;
|
const getEditorState = (state: AppState) => state.ui.editor;
|
||||||
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
|
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
|
||||||
|
|
@ -116,15 +118,14 @@ export const getWidgetCards = createSelector(
|
||||||
|
|
||||||
export const getValidatedDynamicProps = createSelector(
|
export const getValidatedDynamicProps = createSelector(
|
||||||
getDataTree,
|
getDataTree,
|
||||||
getNameBindingsWithData,
|
getParsedDataTree,
|
||||||
(entities: DataTree, nameBindingsWithData: NameBindingsWithData) => {
|
(entities: DataTree, tree) => {
|
||||||
const widgets = { ...entities.canvasWidgets };
|
const widgets = { ...entities.canvasWidgets };
|
||||||
Object.keys(widgets).forEach(widgetKey => {
|
Object.keys(widgets).forEach(widgetKey => {
|
||||||
widgets[widgetKey] = enhanceWithDynamicValuesAndValidations(
|
const evaluatedWidget = _.find(tree, { widgetId: widgetKey });
|
||||||
widgets[widgetKey],
|
if (evaluatedWidget) {
|
||||||
nameBindingsWithData,
|
widgets[widgetKey] = evaluatedWidget;
|
||||||
true,
|
}
|
||||||
);
|
|
||||||
});
|
});
|
||||||
return widgets;
|
return widgets;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,37 +4,42 @@ import { createSelector } from "reselect";
|
||||||
import { getActions, getDataTree } from "./entitiesSelector";
|
import { getActions, getDataTree } from "./entitiesSelector";
|
||||||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||||
import createCachedSelector from "re-reselect";
|
import createCachedSelector from "re-reselect";
|
||||||
|
import { getEvaluatedDataTree } from "utils/DynamicBindingUtils";
|
||||||
|
|
||||||
export type NameBindingsWithData = Record<string, object>;
|
export type NameBindingsWithData = Record<string, object>;
|
||||||
|
|
||||||
export const getNameBindingsWithData = createSelector(
|
export const getNameBindingsWithData = createSelector(
|
||||||
getDataTree,
|
getDataTree,
|
||||||
(dataTree: DataTree): NameBindingsWithData => {
|
(dataTree: DataTree): NameBindingsWithData => {
|
||||||
const nameBindingsWithData: Record<string, object> = {};
|
const nameBindingsWithData: Record<string, object> = {};
|
||||||
Object.keys(dataTree.nameBindings).forEach(key => {
|
Object.keys(dataTree.nameBindings).forEach(key => {
|
||||||
const nameBindings = dataTree.nameBindings[key];
|
const nameBindings = dataTree.nameBindings[key];
|
||||||
const evaluatedValue = JSONPath({
|
nameBindingsWithData[key] = JSONPath({
|
||||||
path: nameBindings,
|
path: nameBindings,
|
||||||
json: dataTree,
|
json: dataTree,
|
||||||
})[0];
|
})[0];
|
||||||
if (evaluatedValue && key !== "undefined") {
|
|
||||||
nameBindingsWithData[key] = evaluatedValue;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return nameBindingsWithData;
|
return nameBindingsWithData;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getParsedDataTree = createSelector(
|
||||||
|
getNameBindingsWithData,
|
||||||
|
(namedBindings: NameBindingsWithData) => {
|
||||||
|
return getEvaluatedDataTree(namedBindings, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// For autocomplete. Use actions cached responses if
|
// For autocomplete. Use actions cached responses if
|
||||||
// there isn't a response already
|
// there isn't a response already
|
||||||
export const getNameBindingsForAutocomplete = createCachedSelector(
|
export const getNameBindingsForAutocomplete = createCachedSelector(
|
||||||
getNameBindingsWithData,
|
getParsedDataTree,
|
||||||
getActions,
|
getActions,
|
||||||
(namedBindings: NameBindingsWithData, actions: ActionDataState["data"]) => {
|
(dataTree: NameBindingsWithData, actions: ActionDataState["data"]) => {
|
||||||
const cachedResponses: Record<string, any> = {};
|
const cachedResponses: Record<string, any> = {};
|
||||||
if (actions && actions.length) {
|
if (actions && actions.length) {
|
||||||
actions.forEach(action => {
|
actions.forEach(action => {
|
||||||
if (!(action.name in namedBindings) && action.cacheResponse) {
|
if (!(action.name in dataTree) && action.cacheResponse) {
|
||||||
try {
|
try {
|
||||||
cachedResponses[action.name] = JSON.parse(action.cacheResponse);
|
cachedResponses[action.name] = JSON.parse(action.cacheResponse);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -43,6 +48,6 @@ export const getNameBindingsForAutocomplete = createCachedSelector(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return { ...namedBindings, ...cachedResponses };
|
return { ...dataTree, ...cachedResponses };
|
||||||
},
|
},
|
||||||
)((state: AppState) => state.entities.actions.data.length);
|
)((state: AppState) => state.entities.actions.data.length);
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,16 @@ import { PropertyPaneReduxState } from "reducers/uiReducers/propertyPaneReducer"
|
||||||
import { PropertyPaneConfigState } from "reducers/entityReducers/propertyPaneConfigReducer";
|
import { PropertyPaneConfigState } from "reducers/entityReducers/propertyPaneConfigReducer";
|
||||||
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||||
import { PropertySection } from "reducers/entityReducers/propertyPaneConfigReducer";
|
import { PropertySection } from "reducers/entityReducers/propertyPaneConfigReducer";
|
||||||
import { enhanceWithDynamicValuesAndValidations } from "utils/DynamicBindingUtils";
|
import {
|
||||||
|
enhanceWidgetWithValidations,
|
||||||
|
getEvaluatedDataTree,
|
||||||
|
} from "utils/DynamicBindingUtils";
|
||||||
import { WidgetProps } from "widgets/BaseWidget";
|
import { WidgetProps } from "widgets/BaseWidget";
|
||||||
import {
|
import {
|
||||||
getNameBindingsWithData,
|
|
||||||
NameBindingsWithData,
|
NameBindingsWithData,
|
||||||
|
getNameBindingsWithData,
|
||||||
} from "./nameBindingsWithDataSelector";
|
} from "./nameBindingsWithDataSelector";
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
const getPropertyPaneState = (state: AppState): PropertyPaneReduxState =>
|
const getPropertyPaneState = (state: AppState): PropertyPaneReduxState =>
|
||||||
state.ui.propertyPane;
|
state.ui.propertyPane;
|
||||||
|
|
@ -41,14 +45,23 @@ export const getWidgetPropsWithValidations = createSelector(
|
||||||
getNameBindingsWithData,
|
getNameBindingsWithData,
|
||||||
(
|
(
|
||||||
widget: WidgetProps | undefined,
|
widget: WidgetProps | undefined,
|
||||||
nameBindigsWithData: NameBindingsWithData,
|
nameBindingsWithData: NameBindingsWithData,
|
||||||
) => {
|
) => {
|
||||||
if (!widget) return undefined;
|
if (!widget) return undefined;
|
||||||
return enhanceWithDynamicValuesAndValidations(
|
const tree = getEvaluatedDataTree(nameBindingsWithData, false);
|
||||||
widget,
|
const evaluatedWidget = _.find(tree, { widgetId: widget.widgetId });
|
||||||
nameBindigsWithData,
|
const validations = enhanceWidgetWithValidations(
|
||||||
false,
|
evaluatedWidget as WidgetProps,
|
||||||
);
|
);
|
||||||
|
if (validations) {
|
||||||
|
const { invalidProps, validationMessages } = validations;
|
||||||
|
return {
|
||||||
|
...widget,
|
||||||
|
invalidProps,
|
||||||
|
validationMessages,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return widget;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
21
app/client/src/utils/DerivedPropertiesFactory.ts
Normal file
21
app/client/src/utils/DerivedPropertiesFactory.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import WidgetFactory from "./WidgetFactory";
|
||||||
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
|
|
||||||
|
export class DerivedPropFactory {
|
||||||
|
static getDerivedPropertiesOfWidgetType(
|
||||||
|
widgetType: WidgetType,
|
||||||
|
widgetName: string,
|
||||||
|
): any {
|
||||||
|
const derivedPropertyMap = WidgetFactory.getWidgetDerivedPropertiesMap(
|
||||||
|
widgetType,
|
||||||
|
);
|
||||||
|
const derivedProps: any = {};
|
||||||
|
Object.keys(derivedPropertyMap).forEach(propertyName => {
|
||||||
|
derivedProps[propertyName] = derivedPropertyMap[propertyName].replace(
|
||||||
|
/this./g,
|
||||||
|
`${widgetName}.`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return derivedProps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,9 @@ import { WidgetProps } from "widgets/BaseWidget";
|
||||||
import { DATA_BIND_REGEX } from "constants/BindingsConstants";
|
import { DATA_BIND_REGEX } from "constants/BindingsConstants";
|
||||||
import ValidationFactory from "./ValidationFactory";
|
import ValidationFactory from "./ValidationFactory";
|
||||||
import JSExecutionManagerSingleton from "jsExecution/JSExecutionManagerSingleton";
|
import JSExecutionManagerSingleton from "jsExecution/JSExecutionManagerSingleton";
|
||||||
import { NameBindingsWithData } from "selectors/nameBindingsWithDataSelector";
|
|
||||||
import unescapeJS from "unescape-js";
|
import unescapeJS from "unescape-js";
|
||||||
|
import { NameBindingsWithData } from "selectors/nameBindingsWithDataSelector";
|
||||||
|
import toposort from "toposort";
|
||||||
|
|
||||||
export const isDynamicValue = (value: string): boolean =>
|
export const isDynamicValue = (value: string): boolean =>
|
||||||
DATA_BIND_REGEX.test(value);
|
DATA_BIND_REGEX.test(value);
|
||||||
|
|
@ -51,11 +52,34 @@ export function parseDynamicString(dynamicString: string): string[] {
|
||||||
return parsedDynamicValues;
|
return parsedDynamicValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAllPaths = (
|
||||||
|
tree: Record<string, any>,
|
||||||
|
prefix = "",
|
||||||
|
): Record<string, true> => {
|
||||||
|
return Object.keys(tree).reduce((res: Record<string, true>, el): Record<
|
||||||
|
string,
|
||||||
|
true
|
||||||
|
> => {
|
||||||
|
if (Array.isArray(tree[el])) {
|
||||||
|
const key = `${prefix}${el}`;
|
||||||
|
return { ...res, [key]: true };
|
||||||
|
} else if (typeof tree[el] === "object" && tree[el] !== null) {
|
||||||
|
const key = `${prefix}${el}`;
|
||||||
|
return { ...res, [key]: true, ...getAllPaths(tree[el], `${key}.`) };
|
||||||
|
} else {
|
||||||
|
const key = `${prefix}${el}`;
|
||||||
|
return { ...res, [key]: true };
|
||||||
|
}
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
export const getDynamicBindings = (
|
export const getDynamicBindings = (
|
||||||
dynamicString: string,
|
dynamicString: string,
|
||||||
): { bindings: string[]; paths: string[] } => {
|
): { bindings: string[]; paths: string[] } => {
|
||||||
|
if (!dynamicString) return { bindings: [], paths: [] };
|
||||||
|
const sanitisedString = dynamicString.trim();
|
||||||
// Get the {{binding}} bound values
|
// Get the {{binding}} bound values
|
||||||
const bindings = parseDynamicString(dynamicString);
|
const bindings = parseDynamicString(sanitisedString);
|
||||||
// Get the "binding" path values
|
// Get the "binding" path values
|
||||||
const paths = bindings.map(binding => {
|
const paths = bindings.map(binding => {
|
||||||
const length = binding.length;
|
const length = binding.length;
|
||||||
|
|
@ -105,22 +129,7 @@ export const getDynamicValue = (
|
||||||
// Get the Data Tree value of those "binding "paths
|
// Get the Data Tree value of those "binding "paths
|
||||||
const values = paths.map((p, i) => {
|
const values = paths.map((p, i) => {
|
||||||
if (p) {
|
if (p) {
|
||||||
const value = evaluateDynamicBoundValue(data, p);
|
return evaluateDynamicBoundValue(data, p);
|
||||||
// Check if the result is a dynamic value, if so get the value again
|
|
||||||
if (isDynamicValue(value)) {
|
|
||||||
// Check for the paths of this dynamic value
|
|
||||||
const { paths } = getDynamicBindings(value);
|
|
||||||
// If it is the same as it came in, log an error
|
|
||||||
// and return the same value back
|
|
||||||
if (paths.length === 1 && paths[0] === p) {
|
|
||||||
console.error("Binding not correct");
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
// Evaluate the value again
|
|
||||||
return getDynamicValue(value, data);
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return bindings[i];
|
return bindings[i];
|
||||||
}
|
}
|
||||||
|
|
@ -134,34 +143,161 @@ export const getDynamicValue = (
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const enhanceWithDynamicValuesAndValidations = (
|
export const enhanceWidgetWithValidations = (
|
||||||
widget: WidgetProps,
|
widget: WidgetProps,
|
||||||
nameBindingsWithData: NameBindingsWithData,
|
|
||||||
replaceWithParsed: boolean,
|
|
||||||
): WidgetProps => {
|
): WidgetProps => {
|
||||||
if (!widget) return widget;
|
if (!widget) return widget;
|
||||||
const properties = { ...widget };
|
const properties = { ...widget };
|
||||||
const invalidProps: Record<string, boolean> = {};
|
const invalidProps: Record<string, boolean> = {};
|
||||||
const validationMessages: Record<string, string> = {};
|
const validationMessages: Record<string, string> = {};
|
||||||
|
Object.keys(properties).forEach((property: string) => {
|
||||||
Object.keys(widget).forEach((property: string) => {
|
const value = properties[property];
|
||||||
let value = widget[property];
|
|
||||||
// Check for dynamic bindings
|
|
||||||
if (widget.dynamicBindings && property in widget.dynamicBindings) {
|
|
||||||
value = getDynamicValue(value, nameBindingsWithData);
|
|
||||||
}
|
|
||||||
// Pass it through validation and parse
|
// Pass it through validation and parse
|
||||||
const {
|
const { isValid, message } = ValidationFactory.validateWidgetProperty(
|
||||||
isValid,
|
widget.type,
|
||||||
parsed,
|
property,
|
||||||
message,
|
value,
|
||||||
} = ValidationFactory.validateWidgetProperty(widget.type, property, value);
|
);
|
||||||
// Store all invalid props
|
// Store all invalid props
|
||||||
if (!isValid) invalidProps[property] = true;
|
if (!isValid) invalidProps[property] = true;
|
||||||
// Store validation Messages
|
// Store validation Messages
|
||||||
if (message) validationMessages[property] = message;
|
if (message) validationMessages[property] = message;
|
||||||
// Replace if flag is turned on
|
|
||||||
if (replaceWithParsed) properties[property] = parsed;
|
|
||||||
});
|
});
|
||||||
return { ...properties, invalidProps, validationMessages };
|
return {
|
||||||
|
...properties,
|
||||||
|
invalidProps,
|
||||||
|
validationMessages,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getParsedTree = (tree: any) => {
|
||||||
|
return Object.keys(tree).reduce((tree, entityKey: string) => {
|
||||||
|
const entity = tree[entityKey];
|
||||||
|
if (entity && entity.type) {
|
||||||
|
const parsedEntity = { ...entity };
|
||||||
|
Object.keys(entity).forEach((property: string) => {
|
||||||
|
const value = entity[property];
|
||||||
|
// Pass it through parse
|
||||||
|
const { parsed } = ValidationFactory.validateWidgetProperty(
|
||||||
|
entity.type,
|
||||||
|
property,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
parsedEntity[property] = parsed;
|
||||||
|
});
|
||||||
|
return { ...tree, [entityKey]: parsedEntity };
|
||||||
|
}
|
||||||
|
return tree;
|
||||||
|
}, tree);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEvaluatedDataTree = (
|
||||||
|
dataTree: NameBindingsWithData,
|
||||||
|
parseValues: boolean,
|
||||||
|
) => {
|
||||||
|
const dynamicDependencyMap = createDependencyTree(dataTree);
|
||||||
|
const evaluatedTree = dependencySortedEvaluateDataTree(
|
||||||
|
dataTree,
|
||||||
|
dynamicDependencyMap,
|
||||||
|
parseValues,
|
||||||
|
);
|
||||||
|
if (parseValues) {
|
||||||
|
return getParsedTree(evaluatedTree);
|
||||||
|
} else {
|
||||||
|
return evaluatedTree;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type DynamicDependencyMap = Record<string, Array<string>>;
|
||||||
|
export const createDependencyTree = (
|
||||||
|
dataTree: NameBindingsWithData,
|
||||||
|
): Array<[string, string]> => {
|
||||||
|
const dependencyMap: DynamicDependencyMap = {};
|
||||||
|
const allKeys = getAllPaths(dataTree);
|
||||||
|
Object.keys(dataTree).forEach(entityKey => {
|
||||||
|
const entity = dataTree[entityKey] as WidgetProps;
|
||||||
|
if (entity && entity.dynamicBindings) {
|
||||||
|
Object.keys(entity.dynamicBindings).forEach(prop => {
|
||||||
|
const { paths } = getDynamicBindings(entity[prop]);
|
||||||
|
dependencyMap[`${entityKey}.${prop}`] = paths.filter(p => !!p);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.keys(dependencyMap).forEach(key => {
|
||||||
|
dependencyMap[key] = _.flatten(
|
||||||
|
dependencyMap[key].map(path => calculateSubDependencies(path, allKeys)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const dependencyTree: Array<[string, string]> = [];
|
||||||
|
Object.keys(dependencyMap).forEach((key: string) => {
|
||||||
|
dependencyMap[key].forEach(dep => dependencyTree.push([key, dep]));
|
||||||
|
});
|
||||||
|
return dependencyTree;
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateSubDependencies = (
|
||||||
|
path: string,
|
||||||
|
all: Record<string, true>,
|
||||||
|
): Array<string> => {
|
||||||
|
const subDeps: Array<string> = [];
|
||||||
|
const identifiers = path.match(/[a-zA-Z_$][a-zA-Z_$0-9.]*/g) || [path];
|
||||||
|
identifiers.forEach((identifier: string) => {
|
||||||
|
if (identifier in all) {
|
||||||
|
subDeps.push(identifier);
|
||||||
|
} else {
|
||||||
|
const subIdentifiers =
|
||||||
|
identifier.match(/[a-zA-Z_$][a-zA-Z_$0-9]*/g) || [];
|
||||||
|
let current = "";
|
||||||
|
for (let i = 0; i < subIdentifiers.length; i++) {
|
||||||
|
const key = `${current}${current ? "." : ""}${subIdentifiers[i]}`;
|
||||||
|
if (key in all) {
|
||||||
|
current = key;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current) subDeps.push(current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return subDeps;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function dependencySortedEvaluateDataTree(
|
||||||
|
dataTree: NameBindingsWithData,
|
||||||
|
dependencyTree: Array<[string, string]>,
|
||||||
|
parseValues: boolean,
|
||||||
|
) {
|
||||||
|
const tree = JSON.parse(JSON.stringify(dataTree));
|
||||||
|
try {
|
||||||
|
// sort dependencies
|
||||||
|
const sortedDependencies = toposort(dependencyTree).reverse();
|
||||||
|
// evaluate and replace values
|
||||||
|
return sortedDependencies.reduce(
|
||||||
|
(currentTree: NameBindingsWithData, path: string) => {
|
||||||
|
const binding = _.get(currentTree as any, path);
|
||||||
|
const widgetType = _.get(
|
||||||
|
currentTree as any,
|
||||||
|
`${path.split(".")[0]}.type`,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
let result = binding;
|
||||||
|
if (isDynamicValue(binding)) {
|
||||||
|
result = getDynamicValue(binding, currentTree);
|
||||||
|
}
|
||||||
|
if (widgetType && parseValues) {
|
||||||
|
const { parsed } = ValidationFactory.validateWidgetProperty(
|
||||||
|
widgetType,
|
||||||
|
`${path.split(".")[1]}`,
|
||||||
|
result,
|
||||||
|
);
|
||||||
|
result = parsed;
|
||||||
|
}
|
||||||
|
return _.set(currentTree, path, result);
|
||||||
|
},
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,11 @@ jest.mock("jsExecution/RealmExecutor", () => {
|
||||||
return { execute: mockExecute, registerLibrary: mockRegisterLibrary };
|
return { execute: mockExecute, registerLibrary: mockRegisterLibrary };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
import { getDynamicValue, parseDynamicString } from "./DynamicBindingUtils";
|
import {
|
||||||
|
dependencySortedEvaluateDataTree,
|
||||||
|
getDynamicValue,
|
||||||
|
parseDynamicString,
|
||||||
|
} from "./DynamicBindingUtils";
|
||||||
import { getNameBindingsWithData } from "selectors/nameBindingsWithDataSelector";
|
import { getNameBindingsWithData } from "selectors/nameBindingsWithDataSelector";
|
||||||
import { AppState, DataTree } from "reducers";
|
import { AppState, DataTree } from "reducers";
|
||||||
|
|
||||||
|
|
@ -121,3 +125,43 @@ it("Parse the dynamic string", () => {
|
||||||
|
|
||||||
expect(value).toEqual(actualValue);
|
expect(value).toEqual(actualValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("evaluates the data tree", () => {
|
||||||
|
const input = {
|
||||||
|
widget1: {
|
||||||
|
displayValue: "{{widget2.computedProperty}}",
|
||||||
|
},
|
||||||
|
widget2: {
|
||||||
|
computedProperty: "{{ widget2.data[widget2.index] }}",
|
||||||
|
data: "{{ apiData.node }}",
|
||||||
|
index: 2,
|
||||||
|
},
|
||||||
|
apiData: {
|
||||||
|
node: ["wrong value", "still wrong", "correct"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const dynamicBindings = [
|
||||||
|
["widget1.displayValue", "widget2.computedProperty"],
|
||||||
|
["widget2.computedProperty", "widget2.data"],
|
||||||
|
["widget2.computedProperty", "widget2.index"],
|
||||||
|
["widget2.data", "apiData.node"],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = {
|
||||||
|
widget1: {
|
||||||
|
displayValue: "correct",
|
||||||
|
},
|
||||||
|
widget2: {
|
||||||
|
computedProperty: "correct",
|
||||||
|
data: ["wrong value", "still wrong", "correct"],
|
||||||
|
index: 2,
|
||||||
|
},
|
||||||
|
apiData: {
|
||||||
|
node: ["wrong value", "still wrong", "correct"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = dependencySortedEvaluateDataTree(input, dynamicBindings);
|
||||||
|
expect(result).toEqual(output);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,33 @@ import {
|
||||||
} from "widgets/BaseWidget";
|
} from "widgets/BaseWidget";
|
||||||
import { WidgetPropertyValidationType } from "./ValidationFactory";
|
import { WidgetPropertyValidationType } from "./ValidationFactory";
|
||||||
|
|
||||||
|
type WidgetDerivedPropertyType = any;
|
||||||
|
export type DerivedPropertiesMap = Record<string, string>;
|
||||||
|
|
||||||
class WidgetFactory {
|
class WidgetFactory {
|
||||||
static widgetMap: Map<WidgetType, WidgetBuilder<WidgetProps>> = new Map();
|
static widgetMap: Map<WidgetType, WidgetBuilder<WidgetProps>> = new Map();
|
||||||
static widgetPropValidationMap: Map<
|
static widgetPropValidationMap: Map<
|
||||||
WidgetType,
|
WidgetType,
|
||||||
WidgetPropertyValidationType
|
WidgetPropertyValidationType
|
||||||
> = new Map();
|
> = new Map();
|
||||||
|
static widgetDerivedPropertiesGetterMap: Map<
|
||||||
|
WidgetType,
|
||||||
|
WidgetDerivedPropertyType
|
||||||
|
> = new Map();
|
||||||
|
static derivedPropertiesMap: Map<
|
||||||
|
WidgetType,
|
||||||
|
DerivedPropertiesMap
|
||||||
|
> = new Map();
|
||||||
|
|
||||||
static registerWidgetBuilder(
|
static registerWidgetBuilder(
|
||||||
widgetType: WidgetType,
|
widgetType: WidgetType,
|
||||||
widgetBuilder: WidgetBuilder<WidgetProps>,
|
widgetBuilder: WidgetBuilder<WidgetProps>,
|
||||||
widgetPropertyValidation: WidgetPropertyValidationType,
|
widgetPropertyValidation: WidgetPropertyValidationType,
|
||||||
|
derivedPropertiesMap: DerivedPropertiesMap,
|
||||||
) {
|
) {
|
||||||
this.widgetMap.set(widgetType, widgetBuilder);
|
this.widgetMap.set(widgetType, widgetBuilder);
|
||||||
this.widgetPropValidationMap.set(widgetType, widgetPropertyValidation);
|
this.widgetPropValidationMap.set(widgetType, widgetPropertyValidation);
|
||||||
|
this.derivedPropertiesMap.set(widgetType, derivedPropertiesMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static createWidget(
|
static createWidget(
|
||||||
|
|
@ -60,6 +73,17 @@ class WidgetFactory {
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getWidgetDerivedPropertiesMap(
|
||||||
|
widgetType: WidgetType,
|
||||||
|
): DerivedPropertiesMap {
|
||||||
|
const map = this.derivedPropertiesMap.get(widgetType);
|
||||||
|
if (!map) {
|
||||||
|
console.error("Widget type validation is not defined");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WidgetCreationException {
|
export interface WidgetCreationException {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import {
|
||||||
WidgetOperations,
|
WidgetOperations,
|
||||||
WidgetOperation,
|
WidgetOperation,
|
||||||
} from "widgets/BaseWidget";
|
} from "widgets/BaseWidget";
|
||||||
import { WidgetType, RenderModes } from "constants/WidgetConstants";
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
import { generateReactKey } from "utils/generators";
|
import { generateReactKey } from "utils/generators";
|
||||||
import {
|
import {
|
||||||
GridDefaults,
|
GridDefaults,
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
} from "constants/WidgetConstants";
|
} from "constants/WidgetConstants";
|
||||||
import { snapToGrid } from "./helpers";
|
import { snapToGrid } from "./helpers";
|
||||||
import { OccupiedSpace } from "constants/editorConstants";
|
import { OccupiedSpace } from "constants/editorConstants";
|
||||||
|
import { DerivedPropFactory } from "utils/DerivedPropertiesFactory";
|
||||||
|
|
||||||
export type WidgetOperationParams = {
|
export type WidgetOperationParams = {
|
||||||
operation: WidgetOperation;
|
operation: WidgetOperation;
|
||||||
|
|
@ -289,17 +290,26 @@ export const generateWidgetProps = (
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const derivedProperties = DerivedPropFactory.getDerivedPropertiesOfWidgetType(
|
||||||
|
type,
|
||||||
|
widgetName,
|
||||||
|
);
|
||||||
|
const dynamicBindings: Record<string, true> = {};
|
||||||
|
Object.keys(derivedProperties).forEach(prop => {
|
||||||
|
dynamicBindings[prop] = true;
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
...widgetConfig,
|
...widgetConfig,
|
||||||
type,
|
type,
|
||||||
widgetName: widgetName,
|
widgetName,
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
parentColumnSpace,
|
parentColumnSpace,
|
||||||
parentRowSpace,
|
parentRowSpace,
|
||||||
renderMode: RenderModes.CANVAS,
|
dynamicBindings,
|
||||||
...sizes,
|
...sizes,
|
||||||
...others,
|
...others,
|
||||||
|
...derivedProperties,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (parent) {
|
if (parent) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ContainerWidget.getPropertyValidationMap(),
|
ContainerWidget.getPropertyValidationMap(),
|
||||||
|
ContainerWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -42,6 +43,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TextWidget.getPropertyValidationMap(),
|
TextWidget.getPropertyValidationMap(),
|
||||||
|
TextWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -52,6 +54,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ButtonWidget.getPropertyValidationMap(),
|
ButtonWidget.getPropertyValidationMap(),
|
||||||
|
ButtonWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -62,6 +65,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SpinnerWidget.getPropertyValidationMap(),
|
SpinnerWidget.getPropertyValidationMap(),
|
||||||
|
SpinnerWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -72,6 +76,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
InputWidget.getPropertyValidationMap(),
|
InputWidget.getPropertyValidationMap(),
|
||||||
|
InputWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -82,6 +87,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CheckboxWidget.getPropertyValidationMap(),
|
CheckboxWidget.getPropertyValidationMap(),
|
||||||
|
CheckboxWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -92,6 +98,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DropdownWidget.getPropertyValidationMap(),
|
DropdownWidget.getPropertyValidationMap(),
|
||||||
|
DropdownWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -102,6 +109,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RadioGroupWidget.getPropertyValidationMap(),
|
RadioGroupWidget.getPropertyValidationMap(),
|
||||||
|
RadioGroupWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
|
|
@ -112,6 +120,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ImageWidget.getPropertyValidationMap(),
|
ImageWidget.getPropertyValidationMap(),
|
||||||
|
ImageWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
"TABLE_WIDGET",
|
"TABLE_WIDGET",
|
||||||
|
|
@ -121,6 +130,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TableWidget.getPropertyValidationMap(),
|
TableWidget.getPropertyValidationMap(),
|
||||||
|
TableWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
"FILE_PICKER_WIDGET",
|
"FILE_PICKER_WIDGET",
|
||||||
|
|
@ -130,6 +140,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FilePickerWidget.getPropertyValidationMap(),
|
FilePickerWidget.getPropertyValidationMap(),
|
||||||
|
FilePickerWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
WidgetFactory.registerWidgetBuilder(
|
WidgetFactory.registerWidgetBuilder(
|
||||||
"DATE_PICKER_WIDGET",
|
"DATE_PICKER_WIDGET",
|
||||||
|
|
@ -139,6 +150,7 @@ class WidgetBuilderRegistry {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DatePickerWidget.getPropertyValidationMap(),
|
DatePickerWidget.getPropertyValidationMap(),
|
||||||
|
DatePickerWidget.getDerivedPropertiesMap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import { PositionTypes } from "constants/WidgetConstants";
|
||||||
|
|
||||||
import ErrorBoundary from "components/editorComponents/ErrorBoundry";
|
import ErrorBoundary from "components/editorComponents/ErrorBoundry";
|
||||||
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
||||||
|
import { DerivedPropertiesMap } from "utils/WidgetFactory";
|
||||||
/***
|
/***
|
||||||
* BaseWidget
|
* BaseWidget
|
||||||
*
|
*
|
||||||
|
|
@ -63,6 +64,10 @@ abstract class BaseWidget<
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDerivedPropertiesMap(): DerivedPropertiesMap {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Widget abstraction to register the widget type
|
* Widget abstraction to register the widget type
|
||||||
* ```javascript
|
* ```javascript
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@ import _ from "lodash";
|
||||||
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
|
||||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||||
|
|
||||||
|
export interface DropDownDerivedProps {
|
||||||
|
selectedOption?: DropdownOption;
|
||||||
|
selectedOptionArr?: DropdownOption[];
|
||||||
|
}
|
||||||
class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
|
class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
|
||||||
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
static getPropertyValidationMap(): WidgetPropertyValidationType {
|
||||||
return {
|
return {
|
||||||
|
|
@ -18,6 +22,23 @@ class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
|
||||||
selectedIndexArr: VALIDATION_TYPES.ARRAY,
|
selectedIndexArr: VALIDATION_TYPES.ARRAY,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
static getDerivedPropertiesMap() {
|
||||||
|
return {
|
||||||
|
selectedOption: `{{
|
||||||
|
this.selectionType === 'SINGLE_SELECT'
|
||||||
|
? this.options[this.selectedIndex]
|
||||||
|
: undefined
|
||||||
|
}}`,
|
||||||
|
selectedOptionArr: `{{
|
||||||
|
const options = this.options || [];
|
||||||
|
this.selectionType === "MULTI_SELECT"
|
||||||
|
? options.filter((opt, index) =>
|
||||||
|
_.includes(this.selectedIndexArr, index),
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
}}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
getPageView() {
|
getPageView() {
|
||||||
return (
|
return (
|
||||||
<DropDownComponent
|
<DropDownComponent
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@ class RadioGroupWidget extends BaseWidget<RadioGroupWidgetProps, WidgetState> {
|
||||||
selectedOptionValue: VALIDATION_TYPES.TEXT,
|
selectedOptionValue: VALIDATION_TYPES.TEXT,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
static getDerivedPropertiesMap() {
|
||||||
|
return {
|
||||||
|
selectedOption:
|
||||||
|
"{{_.find(this.options, { value: this.selectedOptionValue })}}",
|
||||||
|
};
|
||||||
|
}
|
||||||
getPageView() {
|
getPageView() {
|
||||||
return (
|
return (
|
||||||
<RadioGroupComponent
|
<RadioGroupComponent
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import React from "react";
|
||||||
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
|
||||||
import { WidgetType } from "constants/WidgetConstants";
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
import { ActionPayload, TableAction } from "constants/ActionConstants";
|
import { ActionPayload, TableAction } from "constants/ActionConstants";
|
||||||
import _, { forIn } from "lodash";
|
import { forIn } from "lodash";
|
||||||
import TableComponent from "components/designSystems/syncfusion/TableComponent";
|
import TableComponent from "components/designSystems/syncfusion/TableComponent";
|
||||||
|
|
||||||
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
import { VALIDATION_TYPES } from "constants/WidgetValidation";
|
||||||
|
|
@ -32,7 +32,12 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
|
||||||
nextPageKey: VALIDATION_TYPES.TEXT,
|
nextPageKey: VALIDATION_TYPES.TEXT,
|
||||||
prevPageKey: VALIDATION_TYPES.TEXT,
|
prevPageKey: VALIDATION_TYPES.TEXT,
|
||||||
label: VALIDATION_TYPES.TEXT,
|
label: VALIDATION_TYPES.TEXT,
|
||||||
selectedRow: VALIDATION_TYPES.OBJECT,
|
selectedRowIndex: VALIDATION_TYPES.NUMBER,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
static getDerivedPropertiesMap() {
|
||||||
|
return {
|
||||||
|
selectedRow: "{{this.tableData[this.selectedRowIndex]}}",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,40 +51,36 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
|
||||||
isLoading={this.props.isLoading}
|
isLoading={this.props.isLoading}
|
||||||
height={this.state.componentHeight}
|
height={this.state.componentHeight}
|
||||||
width={this.state.componentWidth}
|
width={this.state.componentWidth}
|
||||||
selectedRowIndex={
|
selectedRowIndex={this.props.selectedRowIndex}
|
||||||
this.props.selectedRow && this.props.selectedRow.rowIndex
|
|
||||||
}
|
|
||||||
disableDrag={(disable: boolean) => {
|
disableDrag={(disable: boolean) => {
|
||||||
this.disableDrag(disable);
|
this.disableDrag(disable);
|
||||||
}}
|
}}
|
||||||
onRowClick={(rowData: object, index: number) => {
|
onRowClick={(rowData: object, index: number) => {
|
||||||
const { onRowSelected } = this.props;
|
const { onRowSelected } = this.props;
|
||||||
this.updateSelectedRowProperty(rowData, index);
|
this.updateSelectedRowProperty(index);
|
||||||
|
|
||||||
super.executeAction(onRowSelected);
|
super.executeAction(onRowSelected);
|
||||||
}}
|
}}
|
||||||
></TableComponent>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: TableWidgetProps) {
|
// componentDidUpdate(prevProps: TableWidgetProps) {
|
||||||
super.componentDidUpdate(prevProps);
|
// super.componentDidUpdate(prevProps);
|
||||||
if (
|
// if (
|
||||||
!_.isEqual(prevProps.tableData, this.props.tableData) &&
|
// !_.isEqual(prevProps.tableData, this.props.tableData) &&
|
||||||
prevProps.selectedRow
|
// prevProps.selectedRow
|
||||||
) {
|
// ) {
|
||||||
this.updateSelectedRowProperty(
|
// this.updateSelectedRowProperty(
|
||||||
this.props.tableData[prevProps.selectedRow.rowIndex],
|
// this.props.tableData[prevProps.selectedRow.rowIndex],
|
||||||
prevProps.selectedRow.rowIndex,
|
// prevProps.selectedRow.rowIndex,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
updateSelectedRowProperty(rowData: object, index: number) {
|
updateSelectedRowProperty(index: number) {
|
||||||
const { widgetId } = this.props;
|
const { widgetId } = this.props;
|
||||||
this.updateWidgetProperty(widgetId, "selectedRow", {
|
this.updateWidgetProperty(widgetId, "selectedRowIndex", index);
|
||||||
...rowData,
|
|
||||||
rowIndex: index,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getWidgetType(): WidgetType {
|
getWidgetType(): WidgetType {
|
||||||
|
|
@ -102,7 +103,7 @@ export interface TableWidgetProps extends WidgetProps {
|
||||||
recordActions?: TableAction[];
|
recordActions?: TableAction[];
|
||||||
onPageChange?: ActionPayload[];
|
onPageChange?: ActionPayload[];
|
||||||
onRowSelected?: ActionPayload[];
|
onRowSelected?: ActionPayload[];
|
||||||
selectedRow?: SelectedRow;
|
selectedRowIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TableWidget;
|
export default TableWidget;
|
||||||
|
|
|
||||||
|
|
@ -2569,6 +2569,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf"
|
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf"
|
||||||
integrity sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw==
|
integrity sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw==
|
||||||
|
|
||||||
|
"@types/toposort@^2.0.3":
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/toposort/-/toposort-2.0.3.tgz#dc490842b77c3e910c8d727ff0bdb2fb124cb41b"
|
||||||
|
integrity sha512-jRtyvEu0Na/sy0oIxBW0f6wPQjidgVqlmCTJVHEGTNEUdL1f0YSvdPzHY7nX7MUWAZS6zcAa0KkqofHjy/xDZQ==
|
||||||
|
|
||||||
"@types/uglify-js@*":
|
"@types/uglify-js@*":
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082"
|
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082"
|
||||||
|
|
@ -14649,6 +14654,11 @@ toidentifier@1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||||
|
|
||||||
|
toposort@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
|
||||||
|
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
|
||||||
|
|
||||||
tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0:
|
tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user