Skip to content
Snippets Groups Projects
Unverified Commit 4824543c authored by Ryan Laurie's avatar Ryan Laurie Committed by GitHub
Browse files

Action Creator 5: Use Action Form Settings In Dashboard Modal Forms (#25344)

* derive parameter types from form settings on save

* update template tag types with parameter types

* improve tests

* pass action visualization settings to actionParametersInputForm
parent 5e3da93e
No related branches found
No related tags found
No related merge requests found
Showing
with 108 additions and 60 deletions
......@@ -39,6 +39,7 @@ export interface ActionFormSettings {
fields: {
[tagId: string]: FieldSettings;
};
submitButtonLabel?: string;
confirmMessage?: string;
successMessage?: string;
errorMessage?: string;
......
import { Card } from "metabase-types/api";
import { Card, ActionFormSettings } from "metabase-types/api";
import {
Parameter,
ParameterId,
......@@ -16,6 +16,7 @@ export interface WritebackActionBase {
name: string;
description: string | null;
parameters: WritebackParameter[];
visualization_settings?: ActionFormSettings;
"updated-at": string;
"created-at": string;
}
......
......@@ -41,7 +41,7 @@ const WIDGETS = {
textFile: FormTextFileWidget,
};
function getWidgetComponent(formField) {
export function getWidgetComponent(formField) {
if (typeof formField.type === "string") {
const widget =
WIDGETS[formField.type] || PLUGIN_FORM_WIDGETS[formField.type];
......
......@@ -11,6 +11,7 @@ import type { State } from "metabase-types/store";
import { closeActionParametersModal } from "../actions";
import { getActionParametersModalFormProps } from "../selectors";
import { getFormTitle } from "metabase/writeback/components/ActionCreator/FormCreator";
interface OwnProps {
action: WritebackAction;
......@@ -41,11 +42,14 @@ function ActionParametersInputModal({
action,
closeActionParametersModal,
}: Props) {
const title = getFormTitle(action);
return (
<Modal onClose={closeActionParametersModal}>
<ModalContent title={action.name} onClose={closeActionParametersModal}>
<ModalContent title={title} onClose={closeActionParametersModal}>
<ActionParametersInputForm
{...formProps}
action={action}
onSubmitSuccess={closeActionParametersModal}
/>
</ModalContent>
......
......@@ -79,7 +79,7 @@ function FormItem({
<FormItemWrapper>
<FormItemName>{name}</FormItemName>
<FormSettings>
<FormField type={fieldSettings.inputType} />
<FormField tag={tag} fieldSettings={fieldSettings} />
<FieldSettingsPopover
fieldSettings={fieldSettings}
onChange={onChange}
......
import React from "react";
import { t } from "ttag";
import type { InputType } from "metabase-types/api";
import type { FieldSettings } from "metabase-types/api";
import type { TemplateTag } from "metabase-types/types/Query";
import Input from "metabase/core/components/Input";
import Select from "metabase/core/components/Select";
import FormTextArea from "metabase/admin/datamodel/components/FormTextArea";
import NumericInput from "metabase/core/components/NumericInput";
import DateInput from "metabase/core/components/DateInput";
import Radio from "metabase/core/components/Radio";
import { getWidgetComponent } from "metabase/components/form/FormWidget";
const getSampleOptions = () => [
{ name: t`Option One`, value: 1 },
{ name: t`Option Two`, value: 2 },
{ name: t`Option Three`, value: 3 },
];
import { getFormFieldForParameter } from "./utils";
// sample form fields
export function FormField({ type }: { type: InputType }) {
switch (type) {
case "string":
return <Input />;
case "text":
return <FormTextArea />;
case "number":
return <NumericInput />;
case "date":
return <DateInput />;
case "dropdown":
return <Select options={getSampleOptions()} />;
case "inline-select":
return <Radio options={getSampleOptions()} variant="bubble" />;
default:
return <div>{t`no input selected`}</div>;
}
export function FormField({
tag,
fieldSettings,
}: {
tag: TemplateTag;
fieldSettings: FieldSettings;
}) {
const fieldProps = getFormFieldForParameter(tag, fieldSettings);
const InputField = getWidgetComponent(fieldProps);
return <InputField field={fieldProps} {...fieldProps} />;
}
......@@ -38,14 +38,14 @@ interface InputOptionsMap {
}
const getTextInputs = (): InputOptionType[] => [
{
value: "text",
name: t`long text`,
},
{
value: "string",
name: t`text`,
},
{
value: "text",
name: t`long text`,
},
];
const getSelectInputs = (): InputOptionType[] => [
......
export * from "./FormCreator";
export * from "./utils";
import type { ActionFormSettings, FieldSettings } from "metabase-types/api";
import { t } from "ttag";
import type {
ActionFormSettings,
WritebackAction,
FieldSettings,
} from "metabase-types/api";
import type { Parameter } from "metabase-types/types/Parameter";
import type { TemplateTag } from "metabase-types/types/Query";
export const getDefaultFormSettings = (
overrides: Partial<ActionFormSettings> = {},
......@@ -25,3 +33,52 @@ export const getDefaultFieldSettings = (
width: "medium",
...overrides,
});
const getSampleOptions = () => [
{ name: t`Option One`, value: 1 },
{ name: t`Option Two`, value: 2 },
{ name: t`Option Three`, value: 3 },
];
const getParameterFieldProps = (fieldSettings: FieldSettings) => {
switch (fieldSettings.inputType) {
case "string":
return { type: "input" };
case "text":
return { type: "text" };
case "number":
return { type: "integer" };
case "date":
case "datetime":
case "monthyear":
case "quarteryear":
return { type: "date", values: {} };
case "dropdown":
return {
type: "select",
options: fieldSettings.valueOptions ?? getSampleOptions(),
};
case "inline-select":
return {
type: "radio",
options: fieldSettings.valueOptions ?? getSampleOptions(),
};
default:
return { type: "input" };
}
};
export const getFormFieldForParameter = (
parameter: Parameter | TemplateTag,
fieldSettings: FieldSettings,
) => ({
name: parameter.id,
title: parameter.name,
...getParameterFieldProps(fieldSettings),
});
export const getFormTitle = (action: WritebackAction): string =>
action.visualization_settings?.name || action.name || "Action form";
export const getSubmitButtonLabel = (action: WritebackAction): string =>
action.visualization_settings?.submitButtonLabel || t`Save`;
......@@ -3,16 +3,22 @@ import { connect } from "react-redux";
import { t } from "ttag";
import Form from "metabase/containers/Form";
import {
getFormFieldForParameter,
getSubmitButtonLabel,
} from "metabase/writeback/components/ActionCreator/FormCreator";
import type {
ArbitraryParameterForActionExecution,
WritebackParameter,
WritebackAction,
} from "metabase-types/api";
import type { Parameter, ParameterId } from "metabase-types/types/Parameter";
import type { Dispatch, ReduxAction } from "metabase-types/store";
interface Props {
missingParameters: WritebackParameter[];
action: WritebackAction;
onSubmit: (parameters: ArbitraryParameterForActionExecution[]) => ReduxAction;
onSubmitSuccess: () => void;
dispatch: Dispatch;
......@@ -26,24 +32,6 @@ function getActionParameterType(parameter: Parameter) {
return type;
}
function getParameterFieldProps(parameter: Parameter) {
if (parameter.type === "date/single") {
return { type: "date" };
}
if (parameter.type === "number/=") {
return { type: "integer" };
}
return { type: "input" };
}
function getFormFieldForParameter(parameter: Parameter) {
return {
name: parameter.id,
title: parameter.name,
...getParameterFieldProps(parameter),
};
}
function formatParametersBeforeSubmit(
values: Record<ParameterId, string | number>,
missingParameters: WritebackParameter[],
......@@ -68,15 +56,23 @@ function formatParametersBeforeSubmit(
function ActionParametersInputForm({
missingParameters,
action,
dispatch,
onSubmit,
onSubmitSuccess,
}: Props) {
const fieldSettings = useMemo(
() => action.visualization_settings?.fields ?? {},
[action],
);
const form = useMemo(() => {
return {
fields: missingParameters.map(getFormFieldForParameter),
fields: missingParameters.map(param =>
getFormFieldForParameter(param, fieldSettings[param.id]),
),
};
}, [missingParameters]);
}, [missingParameters, fieldSettings]);
const handleSubmit = useCallback(
params => {
......@@ -90,7 +86,11 @@ function ActionParametersInputForm({
[missingParameters, onSubmit, onSubmitSuccess, dispatch],
);
return <Form form={form} onSubmit={handleSubmit} submitTitle={t`Execute`} />;
const submitButtonLabel = getSubmitButtonLabel(action);
return (
<Form form={form} onSubmit={handleSubmit} submitTitle={submitButtonLabel} />
);
}
export default connect()(ActionParametersInputForm);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment