diff --git a/frontend/src/metabase/public/containers/PublicAction/PublicAction.tsx b/frontend/src/metabase/public/containers/PublicAction/PublicAction.tsx index 25a7b1f3831d460805fb3a2f351b3d534275df24..dedc6f7d2d82c77618a4edc1a0cd3a69066de76c 100644 --- a/frontend/src/metabase/public/containers/PublicAction/PublicAction.tsx +++ b/frontend/src/metabase/public/containers/PublicAction/PublicAction.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from "react"; +import React, { useCallback, useMemo, useState } from "react"; import { useMount } from "react-use"; import title from "metabase/hoc/Title"; @@ -34,20 +34,28 @@ function PublicAction({ action, publicId, onError }: Props) { const hasParameters = action.parameters.length > 0; const successMessage = getSuccessMessage(action); + const formSettings = useMemo(() => { + const actionSettings = action.visualization_settings || {}; + const fieldSettings = + actionSettings.fields || + generateFieldSettingsFromParameters(action.parameters); + return { + ...actionSettings, + fields: fieldSettings, + }; + }, [action]); + const handleSubmit = useCallback( async (values: ParametersForActionExecution) => { try { - const fieldSettings = - action.visualization_settings?.fields || - generateFieldSettingsFromParameters(action.parameters); - const parameters = setNumericValues(values, fieldSettings); + const parameters = setNumericValues(values, formSettings.fields); await PublicApi.executeAction({ uuid: publicId, parameters }); setSubmitted(true); } catch (error) { onError(error as AppErrorDescriptor); } }, - [action, publicId, onError], + [publicId, formSettings, onError], ); useMount(() => { @@ -69,7 +77,7 @@ function PublicAction({ action, publicId, onError }: Props) { <FormTitle>{action.name}</FormTitle> <ActionForm parameters={action.parameters} - formSettings={action.visualization_settings} + formSettings={formSettings} onSubmit={handleSubmit} /> </FormContainer> diff --git a/frontend/src/metabase/public/containers/PublicAction/PublicAction.unit.spec.tsx b/frontend/src/metabase/public/containers/PublicAction/PublicAction.unit.spec.tsx index e4e1e7c05678e018f992e306a4272ff3cc4c8efe..7733475abeaad172ad467aa0ae0e97f8b7c0302c 100644 --- a/frontend/src/metabase/public/containers/PublicAction/PublicAction.unit.spec.tsx +++ b/frontend/src/metabase/public/containers/PublicAction/PublicAction.unit.spec.tsx @@ -119,6 +119,22 @@ describe("PublicAction", () => { expect(screen.getByRole("button", { name: "Submit" })).toBeDisabled(); }); + it("doesn't let to submit until required parameters are filled", async () => { + const action = { + ...TEST_ACTION, + parameters: [SIZE_PARAMETER, { ...COLOR_PARAMETER, required: true }], + }; + await setup({ action }); + + userEvent.type(screen.getByLabelText("Size"), "42"); + expect(screen.getByRole("button", { name: "Submit" })).toBeDisabled(); + + userEvent.type(screen.getByLabelText("Color"), "metablue"); + await waitFor(() => + expect(screen.getByRole("button", { name: "Submit" })).toBeEnabled(), + ); + }); + it("submits form correctly", async () => { const { executeActionEndpointSpy } = await setup({ expectedRequestBody: { diff --git a/frontend/test/metabase/scenarios/models/model-actions.cy.spec.js b/frontend/test/metabase/scenarios/models/model-actions.cy.spec.js index af3d23e07ddaaf0084b30da59e80ec318c5cd361..33dc6ea3e6a68579ca857cb519df1db9353be2ce 100644 --- a/frontend/test/metabase/scenarios/models/model-actions.cy.spec.js +++ b/frontend/test/metabase/scenarios/models/model-actions.cy.spec.js @@ -196,7 +196,7 @@ describe("scenarios > models > actions", () => { cy.visit(url); // Order 1 has quantity 2 by default, so we're not actually mutating data - cy.findByLabelText(/^id/i).type("1"); + cy.findByLabelText("id").type("1"); cy.findByLabelText(/quantity/i).type("2"); cy.findByRole("button", { name: "Submit" }).click();