diff --git a/packages/react/spec/auto/PolarisAutoForm.spec.tsx b/packages/react/spec/auto/PolarisAutoForm.spec.tsx
index c10a5c408..7aa6858bb 100644
--- a/packages/react/spec/auto/PolarisAutoForm.spec.tsx
+++ b/packages/react/spec/auto/PolarisAutoForm.spec.tsx
@@ -6,7 +6,7 @@ import type { UserEvent } from "@testing-library/user-event";
import { userEvent } from "@testing-library/user-event";
import type { ReactNode } from "react";
import React from "react";
-import { TriggerableActionRequiredErrorMessage } from "../../src/auto/AutoFormActionValidators.js";
+import { MissingApiTriggerErrorMessage } from "../../src/auto/AutoFormActionValidators.js";
import { PolarisAutoForm } from "../../src/auto/polaris/PolarisAutoForm.js";
import { PolarisAutoInput } from "../../src/auto/polaris/inputs/PolarisAutoInput.js";
import { PolarisAutoSubmit } from "../../src/auto/polaris/submit/PolarisAutoSubmit.js";
@@ -676,13 +676,13 @@ describe("PolarisAutoForm", () => {
it("throws an error when a model action without triggers", () => {
expect(() => {
render(, { wrapper: PolarisMockedProviders });
- }).toThrow(TriggerableActionRequiredErrorMessage);
+ }).toThrow(MissingApiTriggerErrorMessage);
});
it("throws an error when a global action without triggers", () => {
expect(() => {
render(, { wrapper: PolarisMockedProviders });
- }).toThrow(TriggerableActionRequiredErrorMessage);
+ }).toThrow(MissingApiTriggerErrorMessage);
});
});
describe("Has triggers in api client but no triggers in action metadata", () => {
@@ -690,14 +690,14 @@ describe("PolarisAutoForm", () => {
expect(() => {
render(, { wrapper: PolarisMockedProviders });
loadMockWidgetCreateMetadata({ triggers: [{ specID: "non/api/trigger", __typename: "GadgetTrigger" }] });
- }).toThrow(TriggerableActionRequiredErrorMessage);
+ }).toThrow(MissingApiTriggerErrorMessage);
});
it("throws an error when a global action without triggers", () => {
expect(() => {
render(, { wrapper: PolarisMockedProviders });
loadMockFlipAllMetadata({ triggers: [{ specID: "non/api/trigger", __typename: "GadgetTrigger" }] });
- }).toThrow(TriggerableActionRequiredErrorMessage);
+ }).toThrow(MissingApiTriggerErrorMessage);
});
});
});
diff --git a/packages/react/spec/auto/form/PolarisAutoFormErrors.stories.jsx b/packages/react/spec/auto/form/PolarisAutoFormErrors.stories.jsx
index 99163b13e..992b6937e 100644
--- a/packages/react/spec/auto/form/PolarisAutoFormErrors.stories.jsx
+++ b/packages/react/spec/auto/form/PolarisAutoFormErrors.stories.jsx
@@ -28,6 +28,16 @@ export default {
],
};
+export const InvalidModelAction = {
+ args: {
+ action: api.autoTableTest.invalidAction,
+ },
+};
+
+export const MissingActionProp = {
+ args: {},
+};
+
export const FieldNameCustomParamCollisionError = {
args: {
findBy: "1",
diff --git a/packages/react/spec/auto/table/PolarisAutoTable.spec.tsx b/packages/react/spec/auto/table/PolarisAutoTable.spec.tsx
index e55bce590..1136b6dcb 100644
--- a/packages/react/spec/auto/table/PolarisAutoTable.spec.tsx
+++ b/packages/react/spec/auto/table/PolarisAutoTable.spec.tsx
@@ -3,6 +3,7 @@ import { act, render } from "@testing-library/react";
import type { UserEvent } from "@testing-library/user-event";
import { userEvent } from "@testing-library/user-event";
import React from "react";
+import { InvalidModelErrorMessage } from "../../../src/auto/AutoTableValidators.js";
import { GadgetFieldType } from "../../../src/internal/gql/graphql.js";
import type { TableColumn } from "../../../src/use-table/types.js";
import { testApi as api } from "../../apis.js";
@@ -451,4 +452,14 @@ describe("PolarisAutoTable", () => {
expect(firstRowCells[1].innerHTML).not.toContain("...");
});
});
+
+ describe("invalid model", () => {
+ it("throws an error when the model is not valid", () => {
+ expect(() => {
+ render(, {
+ wrapper: PolarisMockedProviders,
+ });
+ }).toThrow(InvalidModelErrorMessage);
+ });
+ });
});
diff --git a/packages/react/src/auto/AutoForm.ts b/packages/react/src/auto/AutoForm.ts
index 76c8e7dff..4aa9fc9d9 100644
--- a/packages/react/src/auto/AutoForm.ts
+++ b/packages/react/src/auto/AutoForm.ts
@@ -10,7 +10,12 @@ import type { FieldErrors, FieldValues } from "../useActionForm.js";
import { useActionForm } from "../useActionForm.js";
import { get, getFlattenedObjectKeys, type OptionsType } from "../utils.js";
import { validationSchema } from "../validationSchema.js";
-import { validateNonBulkAction, validateTriggersFromApiClient, validateTriggersFromMetadata } from "./AutoFormActionValidators.js";
+import {
+ validateAutoFormProps,
+ validateNonBulkAction,
+ validateTriggersFromApiClient,
+ validateTriggersFromMetadata,
+} from "./AutoFormActionValidators.js";
/** The props that any component accepts */
export type AutoFormProps<
@@ -137,6 +142,7 @@ export const useAutoForm = <
) => {
const { action, record, onSuccess, onFailure, findBy } = props;
+ validateAutoFormProps(props);
validateNonBulkAction(action);
validateTriggersFromApiClient(action);
diff --git a/packages/react/src/auto/AutoFormActionValidators.ts b/packages/react/src/auto/AutoFormActionValidators.ts
index 361911e16..e71eabb69 100644
--- a/packages/react/src/auto/AutoFormActionValidators.ts
+++ b/packages/react/src/auto/AutoFormActionValidators.ts
@@ -1,5 +1,6 @@
import type { ActionFunction, GlobalActionFunction } from "@gadgetinc/api-client-core";
import type { ActionMetadata, GlobalActionMetadata } from "../metadata.js";
+import type { useAutoForm } from "./AutoForm.js";
export const validateNonBulkAction = (action: ActionFunction | GlobalActionFunction) => {
if (action.isBulk) {
@@ -8,14 +9,28 @@ export const validateNonBulkAction = (action: ActionFunction | GlobalActionFunction) => {
if (!validActionTypes.includes(action.type)) {
// When the API client is built with an action without the API trigger, the type will be "stubbedAction"
// action.type === "globalAction" | "action" // Only when the action has the API trigger when the api client is built
- throw new Error(TriggerableActionRequiredErrorMessage);
+ throw new Error(MissingApiTriggerErrorMessage);
+ }
+};
+
+export const validateAutoFormProps = (props: Parameters[0]) => {
+ if (!("action" in props)) {
+ throw new Error(MissingActionPropErrorMessage);
+ }
+
+ if (!props.action) {
+ throw new Error(InvalidActionErrorMessage);
}
};
@@ -32,7 +47,7 @@ export const validateTriggersFromMetadata = (metadata?: ActionMetadata | GlobalA
const hasApiTrigger = triggersAsArray.some((trigger) => trigger.specID === GadgetApiTriggerSpecId);
if (!hasApiTrigger) {
- throw new Error(TriggerableActionRequiredErrorMessage);
+ throw new Error(MissingApiTriggerErrorMessage);
}
}
};
diff --git a/packages/react/src/auto/AutoTableValidators.ts b/packages/react/src/auto/AutoTableValidators.ts
new file mode 100644
index 000000000..a887d2397
--- /dev/null
+++ b/packages/react/src/auto/AutoTableValidators.ts
@@ -0,0 +1,9 @@
+import type { useTable } from "../useTable.js";
+
+export const InvalidModelErrorMessage = `"model" is not a valid Gadget model`;
+
+export const validateAutoTableProps = (manager: Parameters[0]) => {
+ if (!manager) {
+ throw new Error(InvalidModelErrorMessage);
+ }
+};
diff --git a/packages/react/src/auto/mui/MUIAutoForm.tsx b/packages/react/src/auto/mui/MUIAutoForm.tsx
index b32269688..257a3be9a 100644
--- a/packages/react/src/auto/mui/MUIAutoForm.tsx
+++ b/packages/react/src/auto/mui/MUIAutoForm.tsx
@@ -5,7 +5,6 @@ import React from "react";
import { FormProvider } from "react-hook-form";
import { humanizeCamelCase, type OptionsType } from "../../utils.js";
import { useAutoForm, type AutoFormProps } from "../AutoForm.js";
-import { TriggerableActionRequiredErrorMessage } from "../AutoFormActionValidators.js";
import { AutoFormMetadataContext } from "../AutoFormContext.js";
import { MUIAutoInput } from "./inputs/MUIAutoInput.js";
import { MUIAutoSubmit } from "./submit/MUIAutoSubmit.js";
@@ -36,10 +35,6 @@ export const MUIAutoForm = <
) => {
const { action, findBy } = props as MUIAutoFormProps & { findBy: any };
- if (!action) {
- throw new Error(TriggerableActionRequiredErrorMessage);
- }
-
// Component key to force re-render when the action or findBy changes
const componentKey = `${action.modelApiIdentifier ?? ""}.${action.operationName}.${findBy}`;
diff --git a/packages/react/src/auto/polaris/PolarisAutoForm.tsx b/packages/react/src/auto/polaris/PolarisAutoForm.tsx
index 80221b3d8..17e0b35e4 100644
--- a/packages/react/src/auto/polaris/PolarisAutoForm.tsx
+++ b/packages/react/src/auto/polaris/PolarisAutoForm.tsx
@@ -6,7 +6,6 @@ import { FormProvider } from "react-hook-form";
import { humanizeCamelCase, type OptionsType } from "../../utils.js";
import type { AutoFormProps } from "../AutoForm.js";
import { useAutoForm } from "../AutoForm.js";
-import { TriggerableActionRequiredErrorMessage } from "../AutoFormActionValidators.js";
import { AutoFormMetadataContext } from "../AutoFormContext.js";
import { PolarisAutoInput } from "./inputs/PolarisAutoInput.js";
import { PolarisAutoSubmit } from "./submit/PolarisAutoSubmit.js";
@@ -33,10 +32,6 @@ export const PolarisAutoForm = <
const { action, findBy } = props as AutoFormProps &
Omit, "action"> & { findBy: any };
- if (!action) {
- throw new Error(TriggerableActionRequiredErrorMessage);
- }
-
// Component key to force re-render when the action or findBy changes
const componentKey = `${action.modelApiIdentifier ?? ""}.${action.operationName}.${findBy}`;
diff --git a/packages/react/src/auto/polaris/PolarisAutoTable.tsx b/packages/react/src/auto/polaris/PolarisAutoTable.tsx
index 88c8b63f7..938393718 100644
--- a/packages/react/src/auto/polaris/PolarisAutoTable.tsx
+++ b/packages/react/src/auto/polaris/PolarisAutoTable.tsx
@@ -21,6 +21,7 @@ import { useTable } from "../../useTable.js";
import type { ColumnValueType, OptionsType } from "../../utils.js";
import type { AutoTableProps } from "../AutoTable.js";
import { AutoTableContext } from "../AutoTableContext.js";
+import { validateAutoTableProps } from "../AutoTableValidators.js";
import type { BulkActionOption } from "../hooks/useTableBulkActions.js";
import { useTableBulkActions } from "../hooks/useTableBulkActions.js";
import { PolarisAutoBulkActionModal } from "./PolarisAutoBulkActionModal.js";
@@ -65,7 +66,10 @@ export const PolarisAutoTable = <
props: AutoTableProps
) => {
const { model } = props;
- const componentKey = `${[model.findMany.namespace, model.findMany.modelApiIdentifier].join("_")}AutoTable`;
+
+ const componentKey = model
+ ? `${[model.findMany.namespace, model.findMany.modelApiIdentifier].join("_")}AutoTable`
+ : "undefinedModelAutoTable";
return ;
};
@@ -82,6 +86,8 @@ const PolarisAutoTableComponent = <
const searchable = props.searchable ?? true;
const paginate = props.paginate ?? true;
+ validateAutoTableProps(props.model);
+
const [methods, refresh] = useTable(props.model, {
select: props.select,
columns: props.columns,