Show JS eval errors in evaluated value pane and debugger (#4463)
Co-authored-by: jsartisan <pawankumar2901@gmail.com> Co-authored-by: hetunandu <hetu@appsmith.com>
This commit is contained in:
parent
caf7f3678c
commit
4825ce2a2a
|
|
@ -215,8 +215,6 @@ const enumTypeGetter = (
|
||||||
|
|
||||||
type ActionCreatorProps = {
|
type ActionCreatorProps = {
|
||||||
value: string;
|
value: string;
|
||||||
isValid: boolean;
|
|
||||||
validationMessage?: string;
|
|
||||||
onValueChange: (newValue: string) => void;
|
onValueChange: (newValue: string) => void;
|
||||||
additionalAutoComplete?: Record<string, Record<string, unknown>>;
|
additionalAutoComplete?: Record<string, Record<string, unknown>>;
|
||||||
};
|
};
|
||||||
|
|
@ -263,8 +261,6 @@ type SelectorViewProps = ViewProps & {
|
||||||
|
|
||||||
type KeyValueViewProps = ViewProps;
|
type KeyValueViewProps = ViewProps;
|
||||||
type TextViewProps = ViewProps & {
|
type TextViewProps = ViewProps & {
|
||||||
isValid: boolean;
|
|
||||||
validationMessage?: string;
|
|
||||||
additionalAutoComplete?: Record<string, Record<string, unknown>>;
|
additionalAutoComplete?: Record<string, Record<string, unknown>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -307,10 +303,8 @@ const views = {
|
||||||
{props.label && <label>{props.label}</label>}
|
{props.label && <label>{props.label}</label>}
|
||||||
<InputText
|
<InputText
|
||||||
additionalAutocomplete={props.additionalAutoComplete}
|
additionalAutocomplete={props.additionalAutoComplete}
|
||||||
errorMessage={props.validationMessage}
|
|
||||||
evaluatedValue={props.get(props.value, false) as string}
|
evaluatedValue={props.get(props.value, false) as string}
|
||||||
expected={"string"}
|
expected={"string"}
|
||||||
isValid={props.isValid}
|
|
||||||
label={props.label}
|
label={props.label}
|
||||||
onChange={(event: any) => {
|
onChange={(event: any) => {
|
||||||
if (event.target) {
|
if (event.target) {
|
||||||
|
|
@ -769,8 +763,6 @@ function renderField(props: {
|
||||||
value: string;
|
value: string;
|
||||||
field: any;
|
field: any;
|
||||||
label?: string;
|
label?: string;
|
||||||
isValid: boolean;
|
|
||||||
validationMessage?: string;
|
|
||||||
apiOptionTree: TreeDropdownOption[];
|
apiOptionTree: TreeDropdownOption[];
|
||||||
widgetOptionTree: TreeDropdownOption[];
|
widgetOptionTree: TreeDropdownOption[];
|
||||||
queryOptionTree: TreeDropdownOption[];
|
queryOptionTree: TreeDropdownOption[];
|
||||||
|
|
@ -944,8 +936,6 @@ function renderField(props: {
|
||||||
props.onValueChange(finalValueToSet);
|
props.onValueChange(finalValueToSet);
|
||||||
},
|
},
|
||||||
value: props.value,
|
value: props.value,
|
||||||
isValid: props.isValid,
|
|
||||||
validationMessage: props.validationMessage,
|
|
||||||
additionalAutoComplete: props.additionalAutoComplete,
|
additionalAutoComplete: props.additionalAutoComplete,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
@ -961,8 +951,6 @@ function Fields(props: {
|
||||||
value: string;
|
value: string;
|
||||||
fields: any;
|
fields: any;
|
||||||
label?: string;
|
label?: string;
|
||||||
isValid: boolean;
|
|
||||||
validationMessage?: string;
|
|
||||||
apiOptionTree: TreeDropdownOption[];
|
apiOptionTree: TreeDropdownOption[];
|
||||||
widgetOptionTree: TreeDropdownOption[];
|
widgetOptionTree: TreeDropdownOption[];
|
||||||
queryOptionTree: TreeDropdownOption[];
|
queryOptionTree: TreeDropdownOption[];
|
||||||
|
|
@ -995,7 +983,6 @@ function Fields(props: {
|
||||||
apiOptionTree={props.apiOptionTree}
|
apiOptionTree={props.apiOptionTree}
|
||||||
depth={props.depth + 1}
|
depth={props.depth + 1}
|
||||||
fields={field}
|
fields={field}
|
||||||
isValid={props.isValid}
|
|
||||||
label={selectorField.label}
|
label={selectorField.label}
|
||||||
maxDepth={props.maxDepth}
|
maxDepth={props.maxDepth}
|
||||||
modalDropdownList={props.modalDropdownList}
|
modalDropdownList={props.modalDropdownList}
|
||||||
|
|
@ -1007,7 +994,6 @@ function Fields(props: {
|
||||||
}}
|
}}
|
||||||
pageDropdownOptions={props.pageDropdownOptions}
|
pageDropdownOptions={props.pageDropdownOptions}
|
||||||
queryOptionTree={props.queryOptionTree}
|
queryOptionTree={props.queryOptionTree}
|
||||||
validationMessage={props.validationMessage}
|
|
||||||
value={selectorField.value}
|
value={selectorField.value}
|
||||||
widgetOptionTree={props.widgetOptionTree}
|
widgetOptionTree={props.widgetOptionTree}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1039,7 +1025,6 @@ function Fields(props: {
|
||||||
apiOptionTree={props.apiOptionTree}
|
apiOptionTree={props.apiOptionTree}
|
||||||
depth={props.depth + 1}
|
depth={props.depth + 1}
|
||||||
fields={field}
|
fields={field}
|
||||||
isValid={props.isValid}
|
|
||||||
key={index}
|
key={index}
|
||||||
label={selectorField.label}
|
label={selectorField.label}
|
||||||
maxDepth={props.maxDepth}
|
maxDepth={props.maxDepth}
|
||||||
|
|
@ -1052,7 +1037,6 @@ function Fields(props: {
|
||||||
}}
|
}}
|
||||||
pageDropdownOptions={props.pageDropdownOptions}
|
pageDropdownOptions={props.pageDropdownOptions}
|
||||||
queryOptionTree={props.queryOptionTree}
|
queryOptionTree={props.queryOptionTree}
|
||||||
validationMessage={props.validationMessage}
|
|
||||||
value={selectorField.value}
|
value={selectorField.value}
|
||||||
widgetOptionTree={props.widgetOptionTree}
|
widgetOptionTree={props.widgetOptionTree}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1212,13 +1196,11 @@ export function ActionCreator(props: ActionCreatorProps) {
|
||||||
apiOptionTree={apiOptionTree}
|
apiOptionTree={apiOptionTree}
|
||||||
depth={1}
|
depth={1}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
isValid={props.isValid}
|
|
||||||
maxDepth={1}
|
maxDepth={1}
|
||||||
modalDropdownList={modalDropdownList}
|
modalDropdownList={modalDropdownList}
|
||||||
onValueChange={props.onValueChange}
|
onValueChange={props.onValueChange}
|
||||||
pageDropdownOptions={pageDropdownOptions}
|
pageDropdownOptions={pageDropdownOptions}
|
||||||
queryOptionTree={queryOptionTree}
|
queryOptionTree={queryOptionTree}
|
||||||
validationMessage={props.validationMessage}
|
|
||||||
value={props.value}
|
value={props.value}
|
||||||
widgetOptionTree={widgetOptionTree}
|
widgetOptionTree={widgetOptionTree}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ interface Props {
|
||||||
useValidationMessage?: boolean;
|
useValidationMessage?: boolean;
|
||||||
hideEvaluatedValue?: boolean;
|
hideEvaluatedValue?: boolean;
|
||||||
evaluationSubstitutionType?: EvaluationSubstitutionType;
|
evaluationSubstitutionType?: EvaluationSubstitutionType;
|
||||||
|
jsError?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PopoverContentProps {
|
interface PopoverContentProps {
|
||||||
|
|
@ -129,6 +130,7 @@ interface PopoverContentProps {
|
||||||
onMouseLeave: () => void;
|
onMouseLeave: () => void;
|
||||||
hideEvaluatedValue?: boolean;
|
hideEvaluatedValue?: boolean;
|
||||||
preparedStatementViewer: boolean;
|
preparedStatementViewer: boolean;
|
||||||
|
jsError?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PreparedStatementViewerContainer = styled.span`
|
const PreparedStatementViewerContainer = styled.span`
|
||||||
|
|
@ -276,7 +278,9 @@ function PopoverContent(props: PopoverContentProps) {
|
||||||
{props.hasError && (
|
{props.hasError && (
|
||||||
<ErrorText>
|
<ErrorText>
|
||||||
<span className="t--evaluatedPopup-error">
|
<span className="t--evaluatedPopup-error">
|
||||||
{props.useValidationMessage && props.error
|
{props.jsError && props.jsError.length
|
||||||
|
? props.jsError
|
||||||
|
: props.useValidationMessage && props.error
|
||||||
? props.error
|
? props.error
|
||||||
: `This value does not evaluate to type "${props.expected}". Transform it using JS inside '{{ }}'`}
|
: `This value does not evaluate to type "${props.expected}". Transform it using JS inside '{{ }}'`}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -339,6 +343,7 @@ function EvaluatedValuePopup(props: Props) {
|
||||||
expected={props.expected}
|
expected={props.expected}
|
||||||
hasError={props.hasError}
|
hasError={props.hasError}
|
||||||
hideEvaluatedValue={props.hideEvaluatedValue}
|
hideEvaluatedValue={props.hideEvaluatedValue}
|
||||||
|
jsError={props.jsError}
|
||||||
onMouseEnter={() => {
|
onMouseEnter={() => {
|
||||||
setContentHovered(true);
|
setContentHovered(true);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import "codemirror/addon/mode/multiplex";
|
||||||
import "codemirror/addon/tern/tern.css";
|
import "codemirror/addon/tern/tern.css";
|
||||||
import { getDataTreeForAutocomplete } from "selectors/dataTreeSelectors";
|
import { getDataTreeForAutocomplete } from "selectors/dataTreeSelectors";
|
||||||
import EvaluatedValuePopup from "components/editorComponents/CodeEditor/EvaluatedValuePopup";
|
import EvaluatedValuePopup from "components/editorComponents/CodeEditor/EvaluatedValuePopup";
|
||||||
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
|
import { WrappedFieldInputProps } from "redux-form";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import {
|
import {
|
||||||
DataTree,
|
DataTree,
|
||||||
|
|
@ -72,7 +72,6 @@ export type EditorStyleProps = {
|
||||||
leftIcon?: React.ReactNode;
|
leftIcon?: React.ReactNode;
|
||||||
rightIcon?: React.ReactNode;
|
rightIcon?: React.ReactNode;
|
||||||
height?: string | number;
|
height?: string | number;
|
||||||
meta?: Partial<WrappedFieldMetaProps>;
|
|
||||||
showLineNumbers?: boolean;
|
showLineNumbers?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
leftImage?: string;
|
leftImage?: string;
|
||||||
|
|
@ -335,6 +334,38 @@ class CodeEditor extends Component<Props, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPropertyValidation = (
|
||||||
|
dataTree: DataTree,
|
||||||
|
dataTreePath?: string,
|
||||||
|
): {
|
||||||
|
isValid: boolean;
|
||||||
|
validationMessage?: string;
|
||||||
|
jsErrorMessage?: string;
|
||||||
|
} => {
|
||||||
|
if (!dataTreePath) {
|
||||||
|
return { isValid: true, validationMessage: "", jsErrorMessage: "" };
|
||||||
|
}
|
||||||
|
const isValidPath = dataTreePath.replace("evaluatedValues", "invalidProps");
|
||||||
|
const validationMessagePath = dataTreePath.replace(
|
||||||
|
"evaluatedValues",
|
||||||
|
"validationMessages",
|
||||||
|
);
|
||||||
|
const jsErrorMessagePath = dataTreePath.replace(
|
||||||
|
"evaluatedValues",
|
||||||
|
"jsErrorMessages",
|
||||||
|
);
|
||||||
|
|
||||||
|
const isValid = !_.get(dataTree, isValidPath, false);
|
||||||
|
const validationMessage = _.get(
|
||||||
|
dataTree,
|
||||||
|
validationMessagePath,
|
||||||
|
"",
|
||||||
|
) as string;
|
||||||
|
const jsErrorMessage = _.get(dataTree, jsErrorMessagePath, "") as string;
|
||||||
|
|
||||||
|
return { isValid, validationMessage, jsErrorMessage };
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
border,
|
border,
|
||||||
|
|
@ -351,18 +382,23 @@ class CodeEditor extends Component<Props, State> {
|
||||||
hideEvaluatedValue,
|
hideEvaluatedValue,
|
||||||
hoverInteraction,
|
hoverInteraction,
|
||||||
input,
|
input,
|
||||||
meta,
|
|
||||||
placeholder,
|
placeholder,
|
||||||
showLightningMenu,
|
showLightningMenu,
|
||||||
size,
|
size,
|
||||||
theme,
|
theme,
|
||||||
useValidationMessage,
|
useValidationMessage,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const hasError = !!(meta && meta.error);
|
const {
|
||||||
|
isValid,
|
||||||
|
jsErrorMessage,
|
||||||
|
validationMessage,
|
||||||
|
} = this.getPropertyValidation(dynamicData, dataTreePath);
|
||||||
|
const hasError = !isValid || !!jsErrorMessage;
|
||||||
let evaluated = evaluatedValue;
|
let evaluated = evaluatedValue;
|
||||||
if (dataTreePath) {
|
if (dataTreePath) {
|
||||||
evaluated = _.get(dynamicData, dataTreePath);
|
evaluated = _.get(dynamicData, dataTreePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const showEvaluatedValue =
|
const showEvaluatedValue =
|
||||||
this.state.isFocused &&
|
this.state.isFocused &&
|
||||||
("evaluatedValue" in this.props ||
|
("evaluatedValue" in this.props ||
|
||||||
|
|
@ -395,13 +431,14 @@ class CodeEditor extends Component<Props, State> {
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)}
|
)}
|
||||||
<EvaluatedValuePopup
|
<EvaluatedValuePopup
|
||||||
error={meta?.error}
|
error={validationMessage}
|
||||||
evaluatedValue={evaluated}
|
evaluatedValue={evaluated}
|
||||||
evaluationSubstitutionType={evaluationSubstitutionType}
|
evaluationSubstitutionType={evaluationSubstitutionType}
|
||||||
expected={expected}
|
expected={expected}
|
||||||
hasError={hasError}
|
hasError={hasError}
|
||||||
hideEvaluatedValue={hideEvaluatedValue}
|
hideEvaluatedValue={hideEvaluatedValue}
|
||||||
isOpen={showEvaluatedValue}
|
isOpen={showEvaluatedValue}
|
||||||
|
jsError={jsErrorMessage}
|
||||||
theme={theme || EditorTheme.LIGHT}
|
theme={theme || EditorTheme.LIGHT}
|
||||||
useValidationMessage={useValidationMessage}
|
useValidationMessage={useValidationMessage}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ class ActionSelectorControl extends BaseControl<ControlProps> {
|
||||||
return (
|
return (
|
||||||
<ActionCreator
|
<ActionCreator
|
||||||
additionalAutoComplete={this.props.additionalAutoComplete}
|
additionalAutoComplete={this.props.additionalAutoComplete}
|
||||||
isValid={this.props.isValid}
|
|
||||||
onValueChange={this.handleValueUpdate}
|
onValueChange={this.handleValueUpdate}
|
||||||
validationMessage={this.props.errorMessage}
|
|
||||||
value={propertyValue}
|
value={propertyValue}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,14 @@ export interface ControlProps extends ControlData, ControlFunctions {
|
||||||
export interface ControlData
|
export interface ControlData
|
||||||
extends Omit<PropertyPaneControlConfig, "additionalAutoComplete"> {
|
extends Omit<PropertyPaneControlConfig, "additionalAutoComplete"> {
|
||||||
propertyValue?: any;
|
propertyValue?: any;
|
||||||
isValid: boolean;
|
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
expected: string;
|
expected?: string;
|
||||||
evaluatedValue: any;
|
evaluatedValue: any;
|
||||||
validationMessage?: string;
|
|
||||||
widgetProperties: any;
|
widgetProperties: any;
|
||||||
useValidationMessage?: boolean;
|
useValidationMessage?: boolean;
|
||||||
|
parentPropertyName: string;
|
||||||
|
parentPropertyValue: unknown;
|
||||||
|
additionalDynamicData: Record<string, Record<string, unknown>>;
|
||||||
}
|
}
|
||||||
export interface ControlFunctions {
|
export interface ControlFunctions {
|
||||||
onPropertyChange?: (propertyName: string, propertyValue: string) => void;
|
onPropertyChange?: (propertyName: string, propertyValue: string) => void;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { get, has, isString } from "lodash";
|
import { get, isString } from "lodash";
|
||||||
import BaseControl, { ControlProps } from "./BaseControl";
|
import BaseControl, { ControlProps } from "./BaseControl";
|
||||||
import { ControlWrapper, StyledPropertyPaneButton } from "./StyledControls";
|
import { ControlWrapper, StyledPropertyPaneButton } from "./StyledControls";
|
||||||
import styled from "constants/DefaultTheme";
|
import styled from "constants/DefaultTheme";
|
||||||
|
|
@ -81,10 +81,7 @@ type RenderComponentProps = {
|
||||||
index: string;
|
index: string;
|
||||||
item: ChartData;
|
item: ChartData;
|
||||||
length: number;
|
length: number;
|
||||||
validationMessage: {
|
dataTreePath: string;
|
||||||
data: string;
|
|
||||||
seriesName: string;
|
|
||||||
};
|
|
||||||
deleteOption: (index: string) => void;
|
deleteOption: (index: string) => void;
|
||||||
updateOption: (index: string, key: string, value: string) => void;
|
updateOption: (index: string, key: string, value: string) => void;
|
||||||
evaluated: {
|
evaluated: {
|
||||||
|
|
@ -96,13 +93,13 @@ type RenderComponentProps = {
|
||||||
|
|
||||||
function DataControlComponent(props: RenderComponentProps) {
|
function DataControlComponent(props: RenderComponentProps) {
|
||||||
const {
|
const {
|
||||||
|
dataTreePath,
|
||||||
deleteOption,
|
deleteOption,
|
||||||
evaluated,
|
evaluated,
|
||||||
index,
|
index,
|
||||||
item,
|
item,
|
||||||
length,
|
length,
|
||||||
updateOption,
|
updateOption,
|
||||||
validationMessage,
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -121,6 +118,7 @@ function DataControlComponent(props: RenderComponentProps) {
|
||||||
</ActionHolder>
|
</ActionHolder>
|
||||||
<StyledOptionControlWrapper orientation={"HORIZONTAL"}>
|
<StyledOptionControlWrapper orientation={"HORIZONTAL"}>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
|
dataTreePath={`${dataTreePath}.seriesName`}
|
||||||
evaluatedValue={evaluated?.seriesName}
|
evaluatedValue={evaluated?.seriesName}
|
||||||
expected={"string"}
|
expected={"string"}
|
||||||
input={{
|
input={{
|
||||||
|
|
@ -147,6 +145,7 @@ function DataControlComponent(props: RenderComponentProps) {
|
||||||
className={"t--property-control-chart-series-data-control"}
|
className={"t--property-control-chart-series-data-control"}
|
||||||
>
|
>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
|
dataTreePath={`${dataTreePath}.data`}
|
||||||
evaluatedValue={evaluated?.data}
|
evaluatedValue={evaluated?.data}
|
||||||
expected={`Array<x:string, y:number>`}
|
expected={`Array<x:string, y:number>`}
|
||||||
input={{
|
input={{
|
||||||
|
|
@ -161,12 +160,6 @@ function DataControlComponent(props: RenderComponentProps) {
|
||||||
updateOption(index, "data", value);
|
updateOption(index, "data", value);
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
meta={{
|
|
||||||
error: has(validationMessage, "data")
|
|
||||||
? get(validationMessage, "data")
|
|
||||||
: "",
|
|
||||||
touched: true,
|
|
||||||
}}
|
|
||||||
mode={EditorModes.JSON_WITH_BINDING}
|
mode={EditorModes.JSON_WITH_BINDING}
|
||||||
placeholder=""
|
placeholder=""
|
||||||
size={EditorSize.EXTENDED}
|
size={EditorSize.EXTENDED}
|
||||||
|
|
@ -186,7 +179,6 @@ class ChartDataControl extends BaseControl<ControlProps> {
|
||||||
: this.props.propertyValue;
|
: this.props.propertyValue;
|
||||||
|
|
||||||
const dataLength = Object.keys(chartData).length;
|
const dataLength = Object.keys(chartData).length;
|
||||||
const { validationMessage } = this.props;
|
|
||||||
|
|
||||||
const evaluatedValue = this.props.evaluatedValue;
|
const evaluatedValue = this.props.evaluatedValue;
|
||||||
const firstKey = Object.keys(chartData)[0] as string;
|
const firstKey = Object.keys(chartData)[0] as string;
|
||||||
|
|
@ -201,6 +193,7 @@ class ChartDataControl extends BaseControl<ControlProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataControlComponent
|
<DataControlComponent
|
||||||
|
dataTreePath={`${this.props.dataTreePath}.${firstKey}`}
|
||||||
deleteOption={this.deleteOption}
|
deleteOption={this.deleteOption}
|
||||||
evaluated={get(evaluatedValue, `${firstKey}`)}
|
evaluated={get(evaluatedValue, `${firstKey}`)}
|
||||||
index={firstKey}
|
index={firstKey}
|
||||||
|
|
@ -208,7 +201,6 @@ class ChartDataControl extends BaseControl<ControlProps> {
|
||||||
length={1}
|
length={1}
|
||||||
theme={this.props.theme}
|
theme={this.props.theme}
|
||||||
updateOption={this.updateOption}
|
updateOption={this.updateOption}
|
||||||
validationMessage={get(validationMessage, `${firstKey}`)}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -221,6 +213,7 @@ class ChartDataControl extends BaseControl<ControlProps> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataControlComponent
|
<DataControlComponent
|
||||||
|
dataTreePath={`${this.props.dataTreePath}.${key}`}
|
||||||
deleteOption={this.deleteOption}
|
deleteOption={this.deleteOption}
|
||||||
evaluated={get(evaluatedValue, `${key}`)}
|
evaluated={get(evaluatedValue, `${key}`)}
|
||||||
index={key}
|
index={key}
|
||||||
|
|
@ -229,7 +222,6 @@ class ChartDataControl extends BaseControl<ControlProps> {
|
||||||
length={dataLength}
|
length={dataLength}
|
||||||
theme={this.props.theme}
|
theme={this.props.theme}
|
||||||
updateOption={this.updateOption}
|
updateOption={this.updateOption}
|
||||||
validationMessage={get(validationMessage, `${key}`)}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -14,23 +14,20 @@ class CodeEditorControl extends BaseControl<ControlProps> {
|
||||||
dataTreePath,
|
dataTreePath,
|
||||||
evaluatedValue,
|
evaluatedValue,
|
||||||
expected,
|
expected,
|
||||||
isValid,
|
|
||||||
propertyValue,
|
propertyValue,
|
||||||
useValidationMessage,
|
useValidationMessage,
|
||||||
validationMessage,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const props: Partial<ControlProps> = {};
|
const props: Partial<ControlProps> = {};
|
||||||
|
|
||||||
if (dataTreePath) props.dataTreePath = dataTreePath;
|
if (dataTreePath) props.dataTreePath = dataTreePath;
|
||||||
if (evaluatedValue) props.evaluatedValue = evaluatedValue;
|
if (evaluatedValue) props.evaluatedValue = evaluatedValue;
|
||||||
if (expected) props.expected = expected;
|
if (expected) props.expected = expected;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
|
additionalDynamicData={this.props.additionalAutoComplete}
|
||||||
input={{ value: propertyValue, onChange: this.onChange }}
|
input={{ value: propertyValue, onChange: this.onChange }}
|
||||||
meta={{
|
|
||||||
error: isValid ? "" : validationMessage,
|
|
||||||
touched: true,
|
|
||||||
}}
|
|
||||||
mode={EditorModes.TEXT_WITH_BINDING}
|
mode={EditorModes.TEXT_WITH_BINDING}
|
||||||
size={EditorSize.EXTENDED}
|
size={EditorSize.EXTENDED}
|
||||||
tabBehaviour={TabBehaviour.INDENT}
|
tabBehaviour={TabBehaviour.INDENT}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ class ColumnActionSelectorControl extends BaseControl<
|
||||||
<InputTextWrapper>
|
<InputTextWrapper>
|
||||||
<InputText
|
<InputText
|
||||||
evaluatedValue={columnAction.label}
|
evaluatedValue={columnAction.label}
|
||||||
isValid
|
|
||||||
label={columnAction.label}
|
label={columnAction.label}
|
||||||
onChange={this.updateColumnActionLabel.bind(
|
onChange={this.updateColumnActionLabel.bind(
|
||||||
this,
|
this,
|
||||||
|
|
@ -64,12 +63,10 @@ class ColumnActionSelectorControl extends BaseControl<
|
||||||
</InputTextWrapper>
|
</InputTextWrapper>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<ActionCreator
|
<ActionCreator
|
||||||
isValid={(columnAction as any).isValid}
|
|
||||||
onValueChange={this.updateColumnActionFunction.bind(
|
onValueChange={this.updateColumnActionFunction.bind(
|
||||||
this,
|
this,
|
||||||
columnAction,
|
columnAction,
|
||||||
)}
|
)}
|
||||||
validationMessage={(columnAction as any).message}
|
|
||||||
value={columnAction.dynamicTrigger}
|
value={columnAction.dynamicTrigger}
|
||||||
/>
|
/>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ export function InputText(props: {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | string) => void;
|
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | string) => void;
|
||||||
isValid: boolean;
|
|
||||||
errorMessage?: string;
|
|
||||||
evaluatedValue?: any;
|
evaluatedValue?: any;
|
||||||
expected?: string;
|
expected?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
|
@ -44,10 +42,8 @@ export function InputText(props: {
|
||||||
const {
|
const {
|
||||||
additionalDynamicData,
|
additionalDynamicData,
|
||||||
dataTreePath,
|
dataTreePath,
|
||||||
errorMessage,
|
|
||||||
evaluatedValue,
|
evaluatedValue,
|
||||||
expected,
|
expected,
|
||||||
isValid,
|
|
||||||
onChange,
|
onChange,
|
||||||
placeholder,
|
placeholder,
|
||||||
theme,
|
theme,
|
||||||
|
|
@ -64,10 +60,6 @@ export function InputText(props: {
|
||||||
value: value,
|
value: value,
|
||||||
onChange: onChange,
|
onChange: onChange,
|
||||||
}}
|
}}
|
||||||
meta={{
|
|
||||||
error: isValid ? "" : errorMessage,
|
|
||||||
touched: true,
|
|
||||||
}}
|
|
||||||
mode={EditorModes.TEXT_WITH_BINDING}
|
mode={EditorModes.TEXT_WITH_BINDING}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
promptMessage={
|
promptMessage={
|
||||||
|
|
@ -93,11 +85,9 @@ class ComputeTablePropertyControl extends BaseControl<
|
||||||
dataTreePath,
|
dataTreePath,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
expected,
|
expected,
|
||||||
isValid,
|
|
||||||
label,
|
label,
|
||||||
propertyValue,
|
propertyValue,
|
||||||
theme,
|
theme,
|
||||||
validationMessage,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const tableId = this.props.widgetProperties.widgetName;
|
const tableId = this.props.widgetProperties.widgetName;
|
||||||
const value =
|
const value =
|
||||||
|
|
@ -121,9 +111,7 @@ class ComputeTablePropertyControl extends BaseControl<
|
||||||
currentRow,
|
currentRow,
|
||||||
}}
|
}}
|
||||||
dataTreePath={dataTreePath}
|
dataTreePath={dataTreePath}
|
||||||
errorMessage={validationMessage}
|
|
||||||
expected={expected}
|
expected={expected}
|
||||||
isValid={isValid}
|
|
||||||
label={label}
|
label={label}
|
||||||
onChange={this.onTextChange}
|
onChange={this.onTextChange}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,11 @@ import InputTextControl, { InputText } from "./InputTextControl";
|
||||||
class CustomFusionChartControl extends InputTextControl {
|
class CustomFusionChartControl extends InputTextControl {
|
||||||
render() {
|
render() {
|
||||||
const expected = "{\n type: string,\n dataSource: Object\n}";
|
const expected = "{\n type: string,\n dataSource: Object\n}";
|
||||||
const {
|
const { dataTreePath, label, placeholderText, propertyValue } = this.props;
|
||||||
dataTreePath,
|
|
||||||
isValid,
|
|
||||||
label,
|
|
||||||
placeholderText,
|
|
||||||
propertyValue,
|
|
||||||
validationMessage,
|
|
||||||
} = this.props;
|
|
||||||
return (
|
return (
|
||||||
<InputText
|
<InputText
|
||||||
dataTreePath={dataTreePath}
|
dataTreePath={dataTreePath}
|
||||||
errorMessage={validationMessage}
|
|
||||||
expected={expected}
|
expected={expected}
|
||||||
isValid={isValid}
|
|
||||||
label={label}
|
label={label}
|
||||||
onChange={this.onTextChange}
|
onChange={this.onTextChange}
|
||||||
placeholder={placeholderText}
|
placeholder={placeholderText}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ export function InputText(props: {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | string) => void;
|
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | string) => void;
|
||||||
isValid: boolean;
|
|
||||||
errorMessage?: string;
|
|
||||||
evaluatedValue?: any;
|
evaluatedValue?: any;
|
||||||
expected?: string;
|
expected?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
|
@ -26,11 +24,9 @@ export function InputText(props: {
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
dataTreePath,
|
dataTreePath,
|
||||||
errorMessage,
|
|
||||||
evaluatedValue,
|
evaluatedValue,
|
||||||
expected,
|
expected,
|
||||||
hideEvaluatedValue,
|
hideEvaluatedValue,
|
||||||
isValid,
|
|
||||||
onChange,
|
onChange,
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value,
|
||||||
|
|
@ -48,10 +44,6 @@ export function InputText(props: {
|
||||||
value: value,
|
value: value,
|
||||||
onChange: onChange,
|
onChange: onChange,
|
||||||
}}
|
}}
|
||||||
meta={{
|
|
||||||
error: isValid ? "" : errorMessage,
|
|
||||||
touched: true,
|
|
||||||
}}
|
|
||||||
mode={EditorModes.TEXT_WITH_BINDING}
|
mode={EditorModes.TEXT_WITH_BINDING}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
size={EditorSize.EXTENDED}
|
size={EditorSize.EXTENDED}
|
||||||
|
|
@ -70,21 +62,17 @@ class InputTextControl extends BaseControl<InputControlProps> {
|
||||||
defaultValue,
|
defaultValue,
|
||||||
expected,
|
expected,
|
||||||
hideEvaluatedValue,
|
hideEvaluatedValue,
|
||||||
isValid,
|
|
||||||
label,
|
label,
|
||||||
placeholderText,
|
placeholderText,
|
||||||
propertyValue,
|
propertyValue,
|
||||||
validationMessage,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputText
|
<InputText
|
||||||
additionalAutocomplete={additionalAutoComplete}
|
additionalAutocomplete={additionalAutoComplete}
|
||||||
dataTreePath={dataTreePath}
|
dataTreePath={dataTreePath}
|
||||||
errorMessage={validationMessage}
|
|
||||||
expected={expected}
|
expected={expected}
|
||||||
hideEvaluatedValue={hideEvaluatedValue}
|
hideEvaluatedValue={hideEvaluatedValue}
|
||||||
isValid={isValid}
|
|
||||||
label={label}
|
label={label}
|
||||||
onChange={this.onTextChange}
|
onChange={this.onTextChange}
|
||||||
placeholder={placeholderText}
|
placeholder={placeholderText}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ enum LOG_TYPE {
|
||||||
ACTION_EXECUTION_ERROR,
|
ACTION_EXECUTION_ERROR,
|
||||||
ACTION_EXECUTION_SUCCESS,
|
ACTION_EXECUTION_SUCCESS,
|
||||||
ENTITY_DELETED,
|
ENTITY_DELETED,
|
||||||
|
EVAL_ERROR,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LOG_TYPE;
|
export default LOG_TYPE;
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import WIDGET_CONFIG_RESPONSE from "./WidgetConfigResponse";
|
|
||||||
|
|
||||||
describe("WidgetConfigResponse", () => {
|
|
||||||
it("it tests autocomplete child enhancements", () => {
|
|
||||||
const mockProps = {
|
|
||||||
childAutoComplete: "child-autocomplet",
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(
|
|
||||||
WIDGET_CONFIG_RESPONSE.config.LIST_WIDGET.enhancements.child.autocomplete(
|
|
||||||
mockProps,
|
|
||||||
),
|
|
||||||
).toBe(mockProps.childAutoComplete);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("it tests hideEvaluatedValue child enhancements", () => {
|
|
||||||
expect(
|
|
||||||
WIDGET_CONFIG_RESPONSE.config.LIST_WIDGET.enhancements.child.hideEvaluatedValue(),
|
|
||||||
).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("it tests propertyUpdateHook child enhancements with undefined parent widget", () => {
|
|
||||||
const mockParentWidget = {
|
|
||||||
widgetId: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = WIDGET_CONFIG_RESPONSE.config.LIST_WIDGET.enhancements.child.propertyUpdateHook(
|
|
||||||
mockParentWidget,
|
|
||||||
"child-widget-name",
|
|
||||||
"text",
|
|
||||||
"value",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toStrictEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("it tests propertyUpdateHook child enhancements with undefined parent widget", () => {
|
|
||||||
const mockParentWidget = {
|
|
||||||
widgetId: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = WIDGET_CONFIG_RESPONSE.config.LIST_WIDGET.enhancements.child.propertyUpdateHook(
|
|
||||||
mockParentWidget,
|
|
||||||
"child-widget-name",
|
|
||||||
"text",
|
|
||||||
"value",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toStrictEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("it tests propertyUpdateHook child enhancements with defined parent widget", () => {
|
|
||||||
const mockParentWidget = {
|
|
||||||
widgetId: "parent-widget-id",
|
|
||||||
widgetName: "parent-widget-name",
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = WIDGET_CONFIG_RESPONSE.config.LIST_WIDGET.enhancements.child.propertyUpdateHook(
|
|
||||||
mockParentWidget,
|
|
||||||
"child-widget-name",
|
|
||||||
"text",
|
|
||||||
"value",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toStrictEqual([
|
|
||||||
{
|
|
||||||
widgetId: "parent-widget-id",
|
|
||||||
propertyPath: "template.child-widget-name.text",
|
|
||||||
propertyValue: "{{parent-widget-name.items.map((currentItem) => )}}",
|
|
||||||
isDynamicTrigger: false,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -664,7 +664,14 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
autocomplete: (parentProps: any) => {
|
autocomplete: (parentProps: any) => {
|
||||||
return parentProps.childAutoComplete;
|
return parentProps.childAutoComplete;
|
||||||
},
|
},
|
||||||
hideEvaluatedValue: () => true,
|
updateDataTreePath: (parentProps: any, dataTreePath: string) => {
|
||||||
|
return `${
|
||||||
|
parentProps.widgetName
|
||||||
|
}.evaluatedValues.template.${dataTreePath.replace(
|
||||||
|
"evaluatedValues.",
|
||||||
|
"",
|
||||||
|
)}`;
|
||||||
|
},
|
||||||
propertyUpdateHook: (
|
propertyUpdateHook: (
|
||||||
parentProps: any,
|
parentProps: any,
|
||||||
widgetName: string,
|
widgetName: string,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import {
|
||||||
useChildWidgetEnhancementFns,
|
useChildWidgetEnhancementFns,
|
||||||
useParentWithEnhancementFn,
|
useParentWithEnhancementFn,
|
||||||
} from "sagas/WidgetEnhancementHelpers";
|
} from "sagas/WidgetEnhancementHelpers";
|
||||||
|
import { ControlData } from "components/propertyControls/BaseControl";
|
||||||
|
|
||||||
type Props = PropertyPaneControlConfig & {
|
type Props = PropertyPaneControlConfig & {
|
||||||
panel: IPanelProps;
|
panel: IPanelProps;
|
||||||
|
|
@ -51,12 +52,13 @@ const PropertyControl = memo((props: Props) => {
|
||||||
widgetProperties.widgetId,
|
widgetProperties.widgetId,
|
||||||
);
|
);
|
||||||
|
|
||||||
/** get all child enhancments functions */
|
/** get all child enhancements functions */
|
||||||
const {
|
const {
|
||||||
autoCompleteEnhancementFn: childWidgetAutoCompleteEnhancementFn,
|
autoCompleteEnhancementFn: childWidgetAutoCompleteEnhancementFn,
|
||||||
customJSControlEnhancementFn: childWidgetCustomJSControlEnhancementFn,
|
customJSControlEnhancementFn: childWidgetCustomJSControlEnhancementFn,
|
||||||
hideEvaluatedValueEnhancementFn: childWidgetHideEvaluatedValueEnhancementFn,
|
hideEvaluatedValueEnhancementFn: childWidgetHideEvaluatedValueEnhancementFn,
|
||||||
propertyPaneEnhancmentFn: childWidgetPropertyUpdateEnhancementFn,
|
propertyPaneEnhancementFn: childWidgetPropertyUpdateEnhancementFn,
|
||||||
|
updateDataTreePathFn: childWidgetDataTreePathEnhancementFn,
|
||||||
} = useChildWidgetEnhancementFns(widgetProperties.widgetId);
|
} = useChildWidgetEnhancementFns(widgetProperties.widgetId);
|
||||||
|
|
||||||
const toggleDynamicProperty = useCallback(
|
const toggleDynamicProperty = useCallback(
|
||||||
|
|
@ -242,41 +244,28 @@ const PropertyControl = memo((props: Props) => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPropertyValidation = (
|
|
||||||
propertyName: string,
|
|
||||||
): { isValid: boolean; validationMessage?: string } => {
|
|
||||||
let isValid = true;
|
|
||||||
let validationMessage = "";
|
|
||||||
if (widgetProperties) {
|
|
||||||
isValid = widgetProperties.invalidProps
|
|
||||||
? !_.has(widgetProperties.invalidProps, propertyName)
|
|
||||||
: true;
|
|
||||||
const validationMsgPresent =
|
|
||||||
widgetProperties.validationMessages &&
|
|
||||||
_.has(widgetProperties.validationMessages, propertyName);
|
|
||||||
validationMessage = validationMsgPresent
|
|
||||||
? _.get(widgetProperties.validationMessages, propertyName)
|
|
||||||
: "";
|
|
||||||
}
|
|
||||||
return { isValid, validationMessage };
|
|
||||||
};
|
|
||||||
|
|
||||||
const { label, propertyName } = props;
|
const { label, propertyName } = props;
|
||||||
if (widgetProperties) {
|
if (widgetProperties) {
|
||||||
const propertyValue = _.get(widgetProperties, propertyName);
|
const propertyValue = _.get(widgetProperties, propertyName);
|
||||||
const dataTreePath: any = `${widgetProperties.widgetName}.evaluatedValues.${propertyName}`;
|
// get the dataTreePath and apply enhancement if exists
|
||||||
|
// TODO (hetu) make the dataTreePath the actual path of the property
|
||||||
|
// and evaluatedValues should not be added by default
|
||||||
|
let dataTreePath: string | undefined =
|
||||||
|
props.dataTreePath ||
|
||||||
|
`${widgetProperties.widgetName}.evaluatedValues.${propertyName}`;
|
||||||
|
if (childWidgetDataTreePathEnhancementFn) {
|
||||||
|
dataTreePath = childWidgetDataTreePathEnhancementFn(dataTreePath);
|
||||||
|
}
|
||||||
|
|
||||||
const evaluatedValue = _.get(
|
const evaluatedValue = _.get(
|
||||||
widgetProperties,
|
widgetProperties,
|
||||||
`evaluatedValues.${propertyName}`,
|
`evaluatedValues.${propertyName}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { isValid, validationMessage } = getPropertyValidation(propertyName);
|
|
||||||
const { additionalAutoComplete, ...rest } = props;
|
const { additionalAutoComplete, ...rest } = props;
|
||||||
const config = {
|
const config: ControlData = {
|
||||||
...rest,
|
...rest,
|
||||||
isValid,
|
|
||||||
propertyValue,
|
propertyValue,
|
||||||
validationMessage,
|
|
||||||
dataTreePath,
|
dataTreePath,
|
||||||
evaluatedValue,
|
evaluatedValue,
|
||||||
widgetProperties,
|
widgetProperties,
|
||||||
|
|
@ -288,11 +277,12 @@ const PropertyControl = memo((props: Props) => {
|
||||||
additionalDynamicData: {},
|
additionalDynamicData: {},
|
||||||
};
|
};
|
||||||
if (isPathADynamicTrigger(widgetProperties, propertyName)) {
|
if (isPathADynamicTrigger(widgetProperties, propertyName)) {
|
||||||
config.isValid = true;
|
// config.isValid = true;
|
||||||
config.validationMessage = "";
|
config.validationMessage = "";
|
||||||
delete config.dataTreePath;
|
delete config.dataTreePath;
|
||||||
delete config.evaluatedValue;
|
delete config.evaluatedValue;
|
||||||
delete config.expected;
|
delete config.expected;
|
||||||
|
// config.jsErrorMessage = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDynamic: boolean = isPathADynamicProperty(
|
const isDynamic: boolean = isPathADynamicProperty(
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,8 @@ const debuggerReducer = createReducer(initialState, {
|
||||||
|
|
||||||
const entityId = action.payload.source.id;
|
const entityId = action.payload.source.id;
|
||||||
const id =
|
const id =
|
||||||
action.payload.logType === LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR
|
action.payload.logType === LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR ||
|
||||||
|
action.payload.logType === LOG_TYPE.EVAL_ERROR
|
||||||
? `${entityId}-${action.payload.source.propertyPath}`
|
? `${entityId}-${action.payload.source.propertyPath}`
|
||||||
: entityId;
|
: entityId;
|
||||||
const previousState = get(state.errors, id, {});
|
const previousState = get(state.errors, id, {});
|
||||||
|
|
@ -73,7 +74,8 @@ const debuggerReducer = createReducer(initialState, {
|
||||||
|
|
||||||
const entityId = action.payload.source.id;
|
const entityId = action.payload.source.id;
|
||||||
const isWidgetErrorLog =
|
const isWidgetErrorLog =
|
||||||
action.payload.logType === LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR;
|
action.payload.logType === LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR ||
|
||||||
|
action.payload.logType === LOG_TYPE.EVAL_ERROR;
|
||||||
const id = isWidgetErrorLog
|
const id = isWidgetErrorLog
|
||||||
? `${entityId}-${action.payload.source.propertyPath}`
|
? `${entityId}-${action.payload.source.propertyPath}`
|
||||||
: entityId;
|
: entityId;
|
||||||
|
|
|
||||||
|
|
@ -933,6 +933,18 @@ function* executePageLoadAction(pageAction: PageAction) {
|
||||||
message += `\nERROR: "${body}"`;
|
message += `\nERROR: "${body}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppsmithConsole.error({
|
||||||
|
logType: LOG_TYPE.ACTION_EXECUTION_ERROR,
|
||||||
|
text: `Execution failed with status ${response.data.statusCode}`,
|
||||||
|
source: {
|
||||||
|
type: ENTITY_TYPE.ACTION,
|
||||||
|
name: pageAction.name,
|
||||||
|
id: pageAction.id,
|
||||||
|
},
|
||||||
|
state: response.data?.request ?? null,
|
||||||
|
message: JSON.stringify(body),
|
||||||
|
});
|
||||||
|
|
||||||
yield put(
|
yield put(
|
||||||
executeActionError({
|
executeActionError({
|
||||||
actionId: pageAction.id,
|
actionId: pageAction.id,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,12 @@ function* onWidgetUpdateSaga(payload: LogActionPayload) {
|
||||||
const dataTree: DataTree = yield select(getDataTree);
|
const dataTree: DataTree = yield select(getDataTree);
|
||||||
const widget = dataTree[payload.source.name];
|
const widget = dataTree[payload.source.name];
|
||||||
|
|
||||||
if (!isWidget(widget) || !widget.validationMessages) return;
|
if (
|
||||||
|
!isWidget(widget) ||
|
||||||
|
!widget.validationMessages ||
|
||||||
|
!widget.jsErrorMessages
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
// Ignore canvas widget updates
|
// Ignore canvas widget updates
|
||||||
if (widget.type === WidgetTypes.CANVAS_WIDGET) {
|
if (widget.type === WidgetTypes.CANVAS_WIDGET) {
|
||||||
|
|
@ -43,14 +48,17 @@ function* onWidgetUpdateSaga(payload: LogActionPayload) {
|
||||||
|
|
||||||
const validationMessages = widget.validationMessages;
|
const validationMessages = widget.validationMessages;
|
||||||
const validationMessage = validationMessages[propertyPath];
|
const validationMessage = validationMessages[propertyPath];
|
||||||
|
const jsErrorMessages = widget.jsErrorMessages;
|
||||||
|
const jsErrorMessage = jsErrorMessages[propertyPath];
|
||||||
const errors = yield select(getDebuggerErrors);
|
const errors = yield select(getDebuggerErrors);
|
||||||
const errorId = `${source.id}-${propertyPath}`;
|
const errorId = `${source.id}-${propertyPath}`;
|
||||||
const widgetErrorLog = errors[errorId];
|
const widgetErrorLog = errors[errorId];
|
||||||
if (!widgetErrorLog) return;
|
if (!widgetErrorLog) return;
|
||||||
|
|
||||||
const noError = isEmpty(validationMessage);
|
const noError = isEmpty(validationMessage);
|
||||||
|
const noJsError = isEmpty(jsErrorMessage);
|
||||||
|
|
||||||
if (noError) {
|
if (noError && noJsError) {
|
||||||
delete errors[errorId];
|
delete errors[errorId];
|
||||||
|
|
||||||
yield put({
|
yield put({
|
||||||
|
|
@ -123,6 +131,7 @@ function* debuggerLogSaga(action: ReduxAction<Message>) {
|
||||||
yield call(onWidgetUpdateSaga, payload);
|
yield call(onWidgetUpdateSaga, payload);
|
||||||
yield put(debuggerLog(payload));
|
yield put(debuggerLog(payload));
|
||||||
return;
|
return;
|
||||||
|
case LOG_TYPE.EVAL_ERROR:
|
||||||
case LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR:
|
case LOG_TYPE.WIDGET_PROPERTY_VALIDATION_ERROR:
|
||||||
if (payload.source && payload.source.propertyPath) {
|
if (payload.source && payload.source.propertyPath) {
|
||||||
if (payload.text) {
|
if (payload.text) {
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,12 @@ const evalErrorHandler = (errors: EvalError[]) => {
|
||||||
}
|
}
|
||||||
case EvalErrorTypes.EVAL_ERROR: {
|
case EvalErrorTypes.EVAL_ERROR: {
|
||||||
log.debug(error);
|
log.debug(error);
|
||||||
|
AppsmithConsole.error({
|
||||||
|
logType: LOG_TYPE.EVAL_ERROR,
|
||||||
|
text: `The value at ${error.context?.source.propertyPath} is invalid`,
|
||||||
|
message: error.message,
|
||||||
|
source: error.context?.source,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EvalErrorTypes.WIDGET_PROPERTY_VALIDATION_ERROR: {
|
case EvalErrorTypes.WIDGET_PROPERTY_VALIDATION_ERROR: {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ export enum WidgetEnhancementType {
|
||||||
CUSTOM_CONTROL = "child.customJSControl",
|
CUSTOM_CONTROL = "child.customJSControl",
|
||||||
AUTOCOMPLETE = "child.autocomplete",
|
AUTOCOMPLETE = "child.autocomplete",
|
||||||
HIDE_EVALUATED_VALUE = "child.hideEvaluatedValue",
|
HIDE_EVALUATED_VALUE = "child.hideEvaluatedValue",
|
||||||
|
UPDATE_DATA_TREE_PATH = "child.updateDataTreePath",
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getParentWithEnhancementFn(
|
export function getParentWithEnhancementFn(
|
||||||
|
|
@ -161,16 +162,18 @@ export function useChildWidgetEnhancementFn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnhancmentFns = {
|
type EnhancementFns = {
|
||||||
propertyPaneEnhancmentFn: any;
|
updateDataTreePathFn: any;
|
||||||
|
propertyPaneEnhancementFn: any;
|
||||||
autoCompleteEnhancementFn: any;
|
autoCompleteEnhancementFn: any;
|
||||||
customJSControlEnhancementFn: any;
|
customJSControlEnhancementFn: any;
|
||||||
hideEvaluatedValueEnhancementFn: any;
|
hideEvaluatedValueEnhancementFn: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useChildWidgetEnhancementFns(widgetId: string): EnhancmentFns {
|
export function useChildWidgetEnhancementFns(widgetId: string): EnhancementFns {
|
||||||
const enhancmentFns = {
|
const enhancementFns = {
|
||||||
propertyPaneEnhancmentFn: undefined,
|
updateDataTreePathFn: undefined,
|
||||||
|
propertyPaneEnhancementFn: undefined,
|
||||||
autoCompleteEnhancementFn: undefined,
|
autoCompleteEnhancementFn: undefined,
|
||||||
customJSControlEnhancementFn: undefined,
|
customJSControlEnhancementFn: undefined,
|
||||||
hideEvaluatedValueEnhancementFn: undefined,
|
hideEvaluatedValueEnhancementFn: undefined,
|
||||||
|
|
@ -189,8 +192,12 @@ export function useChildWidgetEnhancementFns(widgetId: string): EnhancmentFns {
|
||||||
if (parentWithEnhancementFn) {
|
if (parentWithEnhancementFn) {
|
||||||
// Get the enhancement function based on the enhancementType
|
// Get the enhancement function based on the enhancementType
|
||||||
// from the configs
|
// from the configs
|
||||||
const widgetEnhancmentFns = {
|
const widgetEnhancementFns = {
|
||||||
propertyPaneEnhancmentFn: getWidgetEnhancementFn(
|
updateDataTreePathFn: getWidgetEnhancementFn(
|
||||||
|
parentWithEnhancementFn.type,
|
||||||
|
WidgetEnhancementType.UPDATE_DATA_TREE_PATH,
|
||||||
|
),
|
||||||
|
propertyPaneEnhancementFn: getWidgetEnhancementFn(
|
||||||
parentWithEnhancementFn.type,
|
parentWithEnhancementFn.type,
|
||||||
WidgetEnhancementType.PROPERTY_UPDATE,
|
WidgetEnhancementType.PROPERTY_UPDATE,
|
||||||
),
|
),
|
||||||
|
|
@ -208,16 +215,16 @@ export function useChildWidgetEnhancementFns(widgetId: string): EnhancmentFns {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(widgetEnhancmentFns).map((key: string) => {
|
Object.keys(widgetEnhancementFns).map((key: string) => {
|
||||||
const enhancementFn = get(widgetEnhancmentFns, `${key}`);
|
const enhancementFn = get(widgetEnhancementFns, `${key}`);
|
||||||
|
|
||||||
if (parentDataFromDataTree && enhancementFn) {
|
if (parentDataFromDataTree && enhancementFn) {
|
||||||
set(enhancmentFns, `${key}`, (...args: unknown[]) =>
|
set(enhancementFns, `${key}`, (...args: unknown[]) =>
|
||||||
enhancementFn(parentDataFromDataTree, ...args),
|
enhancementFn(parentDataFromDataTree, ...args),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return enhancmentFns;
|
return enhancementFns;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,14 @@ export const getWidgetPropsForPropertyPane = createSelector(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evaluatedWidget.invalidProps) {
|
if (evaluatedWidget.invalidProps) {
|
||||||
const { invalidProps, validationMessages } = evaluatedWidget;
|
const {
|
||||||
|
invalidProps,
|
||||||
|
jsErrorMessages,
|
||||||
|
validationMessages,
|
||||||
|
} = evaluatedWidget;
|
||||||
widgetProperties.invalidProps = invalidProps;
|
widgetProperties.invalidProps = invalidProps;
|
||||||
widgetProperties.validationMessages = validationMessages;
|
widgetProperties.validationMessages = validationMessages;
|
||||||
|
widgetProperties.jsErrorMessages = jsErrorMessages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return widgetProperties;
|
return widgetProperties;
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ export interface WidgetEvaluatedProps {
|
||||||
invalidProps?: Record<string, boolean>;
|
invalidProps?: Record<string, boolean>;
|
||||||
validationMessages?: Record<string, string>;
|
validationMessages?: Record<string, string>;
|
||||||
evaluatedValues?: Record<string, any>;
|
evaluatedValues?: Record<string, any>;
|
||||||
|
jsErrorMessages?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EntityWithBindings {
|
export interface EntityWithBindings {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ class PropertyControlFactory {
|
||||||
if (customEditor) controlBuilder = this.controlMap.get(customEditor);
|
if (customEditor) controlBuilder = this.controlMap.get(customEditor);
|
||||||
else controlBuilder = this.controlMap.get("CODE_EDITOR");
|
else controlBuilder = this.controlMap.get("CODE_EDITOR");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controlBuilder) {
|
if (controlBuilder) {
|
||||||
const controlProps: ControlProps = {
|
const controlProps: ControlProps = {
|
||||||
...controlData,
|
...controlData,
|
||||||
|
|
|
||||||
|
|
@ -393,6 +393,11 @@ export default class DataTreeEvaluator {
|
||||||
let evalPropertyValue;
|
let evalPropertyValue;
|
||||||
const requiresEval =
|
const requiresEval =
|
||||||
isABindingPath && isDynamicValue(unEvalPropertyValue);
|
isABindingPath && isDynamicValue(unEvalPropertyValue);
|
||||||
|
_.set(
|
||||||
|
currentTree,
|
||||||
|
`${entityName}.jsErrorMessages.${propertyPath}`,
|
||||||
|
"",
|
||||||
|
);
|
||||||
if (requiresEval) {
|
if (requiresEval) {
|
||||||
const evaluationSubstitutionType =
|
const evaluationSubstitutionType =
|
||||||
entity.bindingPaths[propertyPath] ||
|
entity.bindingPaths[propertyPath] ||
|
||||||
|
|
@ -403,6 +408,8 @@ export default class DataTreeEvaluator {
|
||||||
currentTree,
|
currentTree,
|
||||||
evaluationSubstitutionType,
|
evaluationSubstitutionType,
|
||||||
false,
|
false,
|
||||||
|
undefined,
|
||||||
|
fullPropertyPath,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.errors.push({
|
this.errors.push({
|
||||||
|
|
@ -547,6 +554,7 @@ export default class DataTreeEvaluator {
|
||||||
evaluationSubstitutionType: EvaluationSubstitutionType,
|
evaluationSubstitutionType: EvaluationSubstitutionType,
|
||||||
returnTriggers: boolean,
|
returnTriggers: boolean,
|
||||||
callBackData?: Array<any>,
|
callBackData?: Array<any>,
|
||||||
|
fullPropertyPath?: string,
|
||||||
) {
|
) {
|
||||||
// Get the {{binding}} bound values
|
// Get the {{binding}} bound values
|
||||||
const { jsSnippets, stringSegments } = getDynamicBindings(dynamicBinding);
|
const { jsSnippets, stringSegments } = getDynamicBindings(dynamicBinding);
|
||||||
|
|
@ -555,6 +563,7 @@ export default class DataTreeEvaluator {
|
||||||
data,
|
data,
|
||||||
jsSnippets[0],
|
jsSnippets[0],
|
||||||
callBackData,
|
callBackData,
|
||||||
|
fullPropertyPath,
|
||||||
);
|
);
|
||||||
return result.triggers;
|
return result.triggers;
|
||||||
}
|
}
|
||||||
|
|
@ -566,6 +575,7 @@ export default class DataTreeEvaluator {
|
||||||
data,
|
data,
|
||||||
jsSnippet,
|
jsSnippet,
|
||||||
callBackData,
|
callBackData,
|
||||||
|
fullPropertyPath,
|
||||||
);
|
);
|
||||||
return result.result;
|
return result.result;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -592,17 +602,32 @@ export default class DataTreeEvaluator {
|
||||||
data: DataTree,
|
data: DataTree,
|
||||||
js: string,
|
js: string,
|
||||||
callbackData?: Array<any>,
|
callbackData?: Array<any>,
|
||||||
|
fullPropertyPath?: string,
|
||||||
): EvalResult {
|
): EvalResult {
|
||||||
try {
|
try {
|
||||||
return evaluate(js, data, callbackData);
|
return evaluate(js, data, callbackData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (fullPropertyPath) {
|
||||||
|
const { entityName, propertyPath } = getEntityNameAndPropertyPath(
|
||||||
|
fullPropertyPath,
|
||||||
|
);
|
||||||
|
_.set(data, `${entityName}.jsErrorMessages.${propertyPath}`, e.message);
|
||||||
|
const entity = data[entityName];
|
||||||
|
if (isWidget(entity)) {
|
||||||
this.errors.push({
|
this.errors.push({
|
||||||
type: EvalErrorTypes.EVAL_ERROR,
|
type: EvalErrorTypes.EVAL_ERROR,
|
||||||
message: e.message,
|
message: e.message,
|
||||||
context: {
|
context: {
|
||||||
binding: js,
|
source: {
|
||||||
|
id: entity.widgetId,
|
||||||
|
name: entity.widgetName,
|
||||||
|
type: ENTITY_TYPE.WIDGET,
|
||||||
|
propertyPath: propertyPath,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return { result: undefined, triggers: [] };
|
return { result: undefined, triggers: [] };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -624,6 +649,7 @@ export default class DataTreeEvaluator {
|
||||||
EvaluationSubstitutionType.TEMPLATE,
|
EvaluationSubstitutionType.TEMPLATE,
|
||||||
true,
|
true,
|
||||||
undefined,
|
undefined,
|
||||||
|
fullPropertyPath,
|
||||||
);
|
);
|
||||||
valueToValidate = triggers;
|
valueToValidate = triggers;
|
||||||
}
|
}
|
||||||
|
|
@ -642,7 +668,8 @@ export default class DataTreeEvaluator {
|
||||||
: transformed;
|
: transformed;
|
||||||
const safeEvaluatedValue = removeFunctions(evaluatedValue);
|
const safeEvaluatedValue = removeFunctions(evaluatedValue);
|
||||||
_.set(widget, `evaluatedValues.${propertyPath}`, safeEvaluatedValue);
|
_.set(widget, `evaluatedValues.${propertyPath}`, safeEvaluatedValue);
|
||||||
if (!isValid) {
|
const jsError = _.get(widget, `jsErrorMessages.${propertyPath}`);
|
||||||
|
if (!isValid && !jsError) {
|
||||||
this.errors.push({
|
this.errors.push({
|
||||||
type: EvalErrorTypes.WIDGET_PROPERTY_VALIDATION_ERROR,
|
type: EvalErrorTypes.WIDGET_PROPERTY_VALIDATION_ERROR,
|
||||||
message: message || "",
|
message: message || "",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user