Skip to content

Commit

Permalink
chore: Pass errors to widgets (appsmithorg#24760)
Browse files Browse the repository at this point in the history
Fixes appsmithorg#24663

**Summary**

Widget should have access to evaluation errors because that allows it to
make a decision on how to handle errors in property pane and give users
a more visual cue to take a corrective action for fixing errors in the
property pane.

The current pop up isn't too conspicuous to notice and also doesn't
appear when the user deselects the property pane and reselects it.

**Why should this be worked on?**

This aligns with Widget Development API. Accessing the evaluation errors
from evaluations isn't recommended because it is an internal property.
An internal property is not advisable to be used given our roadmap for
community widgets.
Showing default data instead may not be the right way to handle errors
in every use case.
Loom Video describing the requirement :
https://www.loom.com/share/04c228b06bb34b97894e345b46cf0abe?sid=f855ac90-74fa-4ee3-b882-ec70b1b2d9bf
  • Loading branch information
rajatagrawal authored Jul 6, 2023
1 parent 37e4154 commit 6e9e974
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 3 deletions.
77 changes: 75 additions & 2 deletions app/client/src/utils/widgetRenderUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,71 @@
import type { DataTree } from "entities/DataTree/dataTreeFactory";
import type {
DataTree,
WidgetEntity,
WidgetEntityConfig,
} from "entities/DataTree/dataTreeFactory";
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import type { MetaWidgetsReduxState } from "reducers/entityReducers/metaWidgetsReducer";
import { buildChildWidgetTree } from "./widgetRenderUtils";
import { buildChildWidgetTree, createCanvasWidget } from "./widgetRenderUtils";
import type { FlattenedWidgetProps } from "widgets/constants";

describe("createCanvasWidget functionality", () => {
it("returns an empty errors if no evaluations are present", function () {
const canvasWidget = {} as unknown as FlattenedWidgetProps;
const dataTree = {} as unknown as WidgetEntity;

const response = createCanvasWidget(
canvasWidget,
dataTree,
{} as WidgetEntityConfig,
);
expect(response.errors.length).toEqual(0);
});

it("returns an empty errors if no evaluation errors are present", () => {
const canvasWidget = {} as unknown as FlattenedWidgetProps;
const dataTree = {
__evaluation__: {},
} as unknown as WidgetEntity;

const response = createCanvasWidget(
canvasWidget,
dataTree,
{} as WidgetEntityConfig,
);
expect(response.errors.length).toEqual(0);
});

it("populates __evaluation__ errors inside widget error property", () => {
const canvasWidget = {} as unknown as FlattenedWidgetProps;

const dataTree = {
__evaluation__: {
errors: {
text: [
{
errorMessage: {
name: "Validation Error",
message: "Error Message",
},
raw: "Error Message Stack",
},
],
},
},
} as unknown as WidgetEntity;

const response = createCanvasWidget(
canvasWidget,
dataTree,
{} as WidgetEntityConfig,
);
expect(response.errors.length).toEqual(1);
expect(response.errors[0].name).toStrictEqual("Validation Error");
expect(response.errors[0].message).toStrictEqual("Error Message");
expect(response.errors[0].stack).toStrictEqual("Error Message Stack");
expect(response.errors[0].type).toStrictEqual("property");
});
});

describe("test EditorUtils methods", () => {
describe("should test buildChildWidgetTree method", () => {
Expand Down Expand Up @@ -157,6 +221,7 @@ describe("test EditorUtils methods", () => {
value: "test",
widgetId: "3",
widgetName: "three",
errors: [],
},
{
bottomRow: 18,
Expand All @@ -171,6 +236,7 @@ describe("test EditorUtils methods", () => {
value: "test",
widgetId: "4",
widgetName: "four",
errors: [],
},
{
type: "CANVAS",
Expand All @@ -181,6 +247,7 @@ describe("test EditorUtils methods", () => {
bottomRow: 100,
widgetName: "meta_one",
skipForFormWidget: "test",
errors: [],
children: [
{
isDirty: true,
Expand All @@ -195,6 +262,7 @@ describe("test EditorUtils methods", () => {
bottomRow: 10,
widgetName: "meta_two",
skipForFormWidget: "test",
errors: [],
},
],
},
Expand Down Expand Up @@ -229,6 +297,7 @@ describe("test EditorUtils methods", () => {
value: "test",
widgetId: "3",
widgetName: "three",
errors: [],
},
{
bottomRow: 18,
Expand All @@ -242,6 +311,7 @@ describe("test EditorUtils methods", () => {
value: "test",
widgetId: "4",
widgetName: "four",
errors: [],
},
{
isLoading: false,
Expand All @@ -251,6 +321,7 @@ describe("test EditorUtils methods", () => {
widgetId: "1_meta",
bottomRow: 100,
widgetName: "meta_one",
errors: [],
children: [
{
isDirty: true,
Expand All @@ -264,6 +335,7 @@ describe("test EditorUtils methods", () => {
topRow: 0,
bottomRow: 10,
widgetName: "meta_two",
errors: [],
},
],
},
Expand All @@ -277,6 +349,7 @@ describe("test EditorUtils methods", () => {
value: "test",
widgetId: "2",
widgetName: "two",
errors: [],
},
];

Expand Down
36 changes: 35 additions & 1 deletion app/client/src/utils/widgetRenderUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import WidgetFactory from "./WidgetFactory";
import type { WidgetProps } from "widgets/BaseWidget";
import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer";
import type { MetaWidgetsReduxState } from "reducers/entityReducers/metaWidgetsReducer";
import type { WidgetError } from "widgets/BaseWidget";
import { get } from "lodash";
import type { DataTreeError } from "utils/DynamicBindingUtils";
import { EVAL_ERROR_PATH } from "utils/DynamicBindingUtils";

export const createCanvasWidget = (
canvasWidget: FlattenedWidgetProps,
Expand All @@ -40,13 +44,43 @@ export const createCanvasWidget = (
? pick(evaluatedWidget, specificChildProps)
: evaluatedWidget;

return {
const widgetProps = {
...evaluatedStaticProps,
...evaluatedWidgetConfig,
...widgetStaticProps,
} as any;
widgetProps.errors = widgetErrorsFromStaticProps(evaluatedStaticProps);
return widgetProps;
};

function widgetErrorsFromStaticProps(props: Record<string, unknown>) {
/**
* Evaluation Error Map
* {
widgetPropertyName : DataTreeError[]
}
*/

const evaluationErrorMap = get(props, EVAL_ERROR_PATH, {}) as Record<
string,
DataTreeError[]
>;
const evaluationErrors: DataTreeError[] =
Object.values(evaluationErrorMap).flat();
const widgetErrors: WidgetError[] = [];
for (const evalError of evaluationErrors) {
const widgetError: WidgetError = {
name: evalError.errorMessage.name,
message: evalError.errorMessage.message,
stack: evalError.raw,
type: "property",
};

widgetErrors.push(widgetError);
}
return widgetErrors;
}

const WidgetTypes = WidgetFactory.widgetTypes;
export const createLoadingWidget = (
canvasWidget: FlattenedWidgetProps,
Expand Down
7 changes: 7 additions & 0 deletions app/client/src/widgets/BaseWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,12 @@ export const WIDGET_DISPLAY_PROPS = {
isDisabled: true,
backgroundColor: true,
};
export interface WidgetError extends Error {
type: "property" | "configuration" | "other";
}
export interface WidgetErrorProps {
errors?: WidgetError[];
}

export interface WidgetDisplayProps {
//TODO(abhinav): Some of these props are mandatory
Expand All @@ -882,6 +888,7 @@ export interface WidgetDisplayProps {

export interface WidgetDataProps
extends WidgetBaseProps,
WidgetErrorProps,
WidgetPositionProps,
WidgetDisplayProps {}

Expand Down

0 comments on commit 6e9e974

Please sign in to comment.