Form action field base settings (#11035)
- Add settings for text and number fields - Settings are for now the same but I still separated with two components because they will evolve https://github.com/user-attachments/assets/96b7fffd-c3a1-45b9-aeaa-45d63505de3c
This commit is contained in:
@ -4,12 +4,12 @@ import { FormFieldInputRowContainer } from '@/object-record/record-field/form-ty
|
||||
import { TextVariableEditor } from '@/object-record/record-field/form-types/components/TextVariableEditor';
|
||||
import { useTextVariableEditor } from '@/object-record/record-field/form-types/hooks/useTextVariableEditor';
|
||||
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
|
||||
import { InputErrorHelper } from '@/ui/input/components/InputErrorHelper';
|
||||
import { InputHint } from '@/ui/input/components/InputHint';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { parseEditorContent } from '@/workflow/workflow-variables/utils/parseEditorContent';
|
||||
import { useId } from 'react';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import { InputErrorHelper } from '@/ui/input/components/InputErrorHelper';
|
||||
import { InputHint } from '@/ui/input/components/InputHint';
|
||||
|
||||
type FormTextFieldInputProps = {
|
||||
label?: string;
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { Editor, EditorContent } from '@tiptap/react';
|
||||
|
||||
const StyledEditor = styled.div<{ multiline?: boolean; readonly?: boolean }>`
|
||||
const StyledEditor = styled.div<{
|
||||
multiline?: boolean;
|
||||
readonly?: boolean;
|
||||
}>`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
|
||||
@ -88,9 +88,12 @@ export const workflowFormActionSettingsSchema =
|
||||
z.object({
|
||||
id: z.string().uuid(),
|
||||
label: z.string(),
|
||||
type: z.nativeEnum(FieldMetadataType),
|
||||
type: z.union([
|
||||
z.literal(FieldMetadataType.TEXT),
|
||||
z.literal(FieldMetadataType.NUMBER),
|
||||
]),
|
||||
placeholder: z.string().optional(),
|
||||
settings: z.record(z.any()),
|
||||
settings: z.record(z.any()).optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@ -4,9 +4,9 @@ import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrTh
|
||||
import { WorkflowEditActionCreateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord';
|
||||
import { WorkflowEditActionDeleteRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord';
|
||||
import { WorkflowEditActionFindRecords } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords';
|
||||
import { WorkflowEditActionForm } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm';
|
||||
import { WorkflowEditActionSendEmail } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail';
|
||||
import { WorkflowEditActionUpdateRecord } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord';
|
||||
import { WorkflowEditActionForm } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionForm';
|
||||
import { WorkflowEditTriggerCronForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerCronForm';
|
||||
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm';
|
||||
import { WorkflowEditTriggerManualForm } from '@/workflow/workflow-trigger/components/WorkflowEditTriggerManualForm';
|
||||
|
||||
@ -5,11 +5,13 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { WorkflowFormAction } from '@/workflow/types/Workflow';
|
||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { WorkflowEditActionFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFieldSettings';
|
||||
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
|
||||
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { FieldMetadataType, isDefined } from 'twenty-shared';
|
||||
import {
|
||||
IconChevronDown,
|
||||
@ -18,9 +20,11 @@ import {
|
||||
IconTrash,
|
||||
useIcons,
|
||||
} from 'twenty-ui';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
type WorkflowEditActionFormProps = {
|
||||
export type WorkflowEditActionFormProps = {
|
||||
action: WorkflowFormAction;
|
||||
actionOptions:
|
||||
| {
|
||||
@ -32,6 +36,16 @@ type WorkflowEditActionFormProps = {
|
||||
};
|
||||
};
|
||||
|
||||
export type WorkflowFormActionField = {
|
||||
id: string;
|
||||
label: string;
|
||||
type: FieldMetadataType.TEXT | FieldMetadataType.NUMBER;
|
||||
placeholder?: string;
|
||||
settings?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
type FormData = WorkflowFormActionField[];
|
||||
|
||||
const StyledRowContainer = styled.div`
|
||||
column-gap: ${({ theme }) => theme.spacing(1)};
|
||||
display: grid;
|
||||
@ -92,6 +106,9 @@ export const WorkflowEditActionForm = ({
|
||||
const theme = useTheme();
|
||||
const { getIcon } = useIcons();
|
||||
const { t } = useLingui();
|
||||
|
||||
const [formData, setFormData] = useState<FormData>(action.settings.input);
|
||||
|
||||
const headerTitle = isDefined(action.name) ? action.name : `Form`;
|
||||
const headerIcon = getActionIcon(action.type);
|
||||
const [selectedField, setSelectedField] = useState<string | null>(null);
|
||||
@ -108,6 +125,40 @@ export const WorkflowEditActionForm = ({
|
||||
}
|
||||
};
|
||||
|
||||
const onFieldUpdate = (updatedField: WorkflowFormActionField) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedFormData = formData.map((currentField) =>
|
||||
currentField.id === updatedField.id ? updatedField : currentField,
|
||||
);
|
||||
|
||||
setFormData(updatedFormData);
|
||||
|
||||
saveAction(updatedFormData);
|
||||
};
|
||||
|
||||
const saveAction = useDebouncedCallback(async (formData: FormData) => {
|
||||
if (actionOptions.readonly === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
actionOptions.onActionUpdate({
|
||||
...action,
|
||||
settings: {
|
||||
...action.settings,
|
||||
input: formData,
|
||||
},
|
||||
});
|
||||
}, 1_000);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
saveAction.flush();
|
||||
};
|
||||
}, [saveAction]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WorkflowStepHeader
|
||||
@ -128,7 +179,7 @@ export const WorkflowEditActionForm = ({
|
||||
disabled={actionOptions.readonly}
|
||||
/>
|
||||
<WorkflowStepBody>
|
||||
{action.settings.input.map((field) => (
|
||||
{formData.map((field) => (
|
||||
<FormFieldInputContainer key={field.id}>
|
||||
{field.label ? <InputLabel>{field.label}</InputLabel> : null}
|
||||
|
||||
@ -166,19 +217,32 @@ export const WorkflowEditActionForm = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedFormData = formData.filter(
|
||||
(currentField) => currentField.id !== field.id,
|
||||
);
|
||||
|
||||
setFormData(updatedFormData);
|
||||
|
||||
actionOptions.onActionUpdate({
|
||||
...action,
|
||||
settings: {
|
||||
...action.settings,
|
||||
input: action.settings.input.filter(
|
||||
(f) => f.id !== field.id,
|
||||
),
|
||||
input: updatedFormData,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledIconButtonContainer>
|
||||
)}
|
||||
{isFieldSelected(field.id) && (
|
||||
<WorkflowEditActionFormFieldSettings
|
||||
field={field}
|
||||
onChange={onFieldUpdate}
|
||||
onClose={() => {
|
||||
setSelectedField(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</StyledRowContainer>
|
||||
</FormFieldInputContainer>
|
||||
))}
|
||||
@ -189,20 +253,24 @@ export const WorkflowEditActionForm = ({
|
||||
<FormFieldInputInputContainer
|
||||
hasRightElement={false}
|
||||
onClick={() => {
|
||||
const { label, placeholder } = getDefaultFormFieldSettings(
|
||||
FieldMetadataType.TEXT,
|
||||
);
|
||||
|
||||
const newField: WorkflowFormActionField = {
|
||||
id: v4(),
|
||||
type: FieldMetadataType.TEXT,
|
||||
label,
|
||||
placeholder,
|
||||
};
|
||||
|
||||
setFormData([...formData, newField]);
|
||||
|
||||
actionOptions.onActionUpdate({
|
||||
...action,
|
||||
settings: {
|
||||
...action.settings,
|
||||
input: [
|
||||
...action.settings.input,
|
||||
{
|
||||
id: v4(),
|
||||
type: FieldMetadataType.TEXT,
|
||||
label: 'New Field',
|
||||
placeholder: 'New Field',
|
||||
settings: {},
|
||||
},
|
||||
],
|
||||
input: [...action.settings.input, newField],
|
||||
},
|
||||
});
|
||||
}}
|
||||
@ -0,0 +1,140 @@
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
||||
import { FormSelectFieldInput } from '@/object-record/record-field/form-types/components/FormSelectFieldInput';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionForm';
|
||||
import { WorkflowFormFieldSettingsByType } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsByType';
|
||||
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
import {
|
||||
IconSettingsAutomation,
|
||||
IconX,
|
||||
IllustrationIconNumbers,
|
||||
IllustrationIconText,
|
||||
LightIconButton,
|
||||
} from 'twenty-ui';
|
||||
|
||||
type WorkflowEditActionFormFieldSettingsProps = {
|
||||
field: WorkflowFormActionField;
|
||||
onChange: (field: WorkflowFormActionField) => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const StyledFormFieldSettingsContainer = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
margin-top: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledSettingsContent = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledSettingsHeader = styled.div`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
display: grid;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding-inline: ${({ theme }) => theme.spacing(3)};
|
||||
grid-template-columns: 1fr 24px;
|
||||
padding-bottom: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledTitleContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding-top: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledCloseButtonContainer = styled.div`
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const WorkflowEditActionFormFieldSettings = ({
|
||||
field,
|
||||
onChange,
|
||||
onClose,
|
||||
}: WorkflowEditActionFormFieldSettingsProps) => {
|
||||
const theme = useTheme();
|
||||
const onSubFieldUpdate = (fieldName: string, value: any) => {
|
||||
onChange({
|
||||
...field,
|
||||
[fieldName]: value,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledFormFieldSettingsContainer>
|
||||
<StyledSettingsHeader>
|
||||
<StyledTitleContainer>
|
||||
<IconSettingsAutomation
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.primary}
|
||||
/>
|
||||
{t`Input settings`}
|
||||
</StyledTitleContainer>
|
||||
<StyledCloseButtonContainer>
|
||||
<LightIconButton
|
||||
Icon={IconX}
|
||||
size="small"
|
||||
accent="secondary"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</StyledCloseButtonContainer>
|
||||
</StyledSettingsHeader>
|
||||
<StyledSettingsContent>
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>Type</InputLabel>
|
||||
<FormSelectFieldInput
|
||||
options={[
|
||||
{
|
||||
label: getDefaultFormFieldSettings(FieldMetadataType.TEXT)
|
||||
.label,
|
||||
value: FieldMetadataType.TEXT,
|
||||
icon: IllustrationIconText,
|
||||
},
|
||||
{
|
||||
label: getDefaultFormFieldSettings(FieldMetadataType.NUMBER)
|
||||
.label,
|
||||
value: FieldMetadataType.NUMBER,
|
||||
icon: IllustrationIconNumbers,
|
||||
},
|
||||
]}
|
||||
onPersist={(newType: string | null) => {
|
||||
if (newType === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const type = newType as
|
||||
| FieldMetadataType.TEXT
|
||||
| FieldMetadataType.NUMBER;
|
||||
const { label, placeholder } = getDefaultFormFieldSettings(type);
|
||||
|
||||
onChange({
|
||||
...field,
|
||||
type,
|
||||
label,
|
||||
placeholder,
|
||||
});
|
||||
}}
|
||||
defaultValue={field.type}
|
||||
preventDisplayPadding
|
||||
/>
|
||||
</FormFieldInputContainer>
|
||||
<WorkflowFormFieldSettingsByType
|
||||
field={field}
|
||||
onChange={onSubFieldUpdate}
|
||||
/>
|
||||
</StyledSettingsContent>
|
||||
</StyledFormFieldSettingsContainer>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,37 @@
|
||||
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionForm';
|
||||
import { assertUnreachable, FieldMetadataType } from 'twenty-shared';
|
||||
import { WorkflowFormFieldSettingsNumber } from './WorkflowFormFieldSettingsNumber';
|
||||
import { WorkflowFormFieldSettingsText } from './WorkflowFormFieldSettingsText';
|
||||
|
||||
export const WorkflowFormFieldSettingsByType = ({
|
||||
field,
|
||||
onChange,
|
||||
}: {
|
||||
field: WorkflowFormActionField;
|
||||
onChange: (fieldName: string, value: string | null) => void;
|
||||
}) => {
|
||||
switch (field.type) {
|
||||
case FieldMetadataType.TEXT:
|
||||
return (
|
||||
<WorkflowFormFieldSettingsText
|
||||
label={field.label}
|
||||
placeholder={field.placeholder}
|
||||
onChange={(fieldName, value) => {
|
||||
onChange(fieldName, value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case FieldMetadataType.NUMBER:
|
||||
return (
|
||||
<WorkflowFormFieldSettingsNumber
|
||||
label={field.label}
|
||||
placeholder={field.placeholder}
|
||||
onChange={(fieldName, value) => {
|
||||
onChange(fieldName, value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return assertUnreachable(field.type, 'Unknown form field type');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,48 @@
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
type WorkflowFormFieldSettingsNumberProps = {
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
onChange: (fieldName: string, value: string | null) => void;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const WorkflowFormFieldSettingsNumber = ({
|
||||
label,
|
||||
placeholder,
|
||||
onChange,
|
||||
}: WorkflowFormFieldSettingsNumberProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>Label</InputLabel>
|
||||
<FormTextFieldInput
|
||||
onPersist={(newLabel: string | null) => {
|
||||
onChange('label', newLabel);
|
||||
}}
|
||||
defaultValue={label ?? t`Number`}
|
||||
placeholder={t`Text`}
|
||||
/>
|
||||
</FormFieldInputContainer>
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>Placeholder</InputLabel>
|
||||
<FormTextFieldInput
|
||||
onPersist={(newPlaceholder: string | null) => {
|
||||
onChange('placeholder', newPlaceholder);
|
||||
}}
|
||||
defaultValue={placeholder ?? '1000'}
|
||||
placeholder={'1000'}
|
||||
/>
|
||||
</FormFieldInputContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,47 @@
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type WorkflowFormFieldSettingsTextProps = {
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
onChange: (fieldName: string, value: string | null) => void;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const WorkflowFormFieldSettingsText = ({
|
||||
label,
|
||||
placeholder,
|
||||
onChange,
|
||||
}: WorkflowFormFieldSettingsTextProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>Label</InputLabel>
|
||||
<FormTextFieldInput
|
||||
onPersist={(newLabel: string | null) => {
|
||||
onChange('label', newLabel);
|
||||
}}
|
||||
defaultValue={label}
|
||||
placeholder={'Text'}
|
||||
/>
|
||||
</FormFieldInputContainer>
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>Placeholder</InputLabel>
|
||||
<FormTextFieldInput
|
||||
onPersist={(newPlaceholder: string | null) => {
|
||||
onChange('placeholder', newPlaceholder);
|
||||
}}
|
||||
defaultValue={placeholder}
|
||||
placeholder={'Enter your text'}
|
||||
/>
|
||||
</FormFieldInputContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import { WorkflowFormAction } from '@/workflow/types/Workflow';
|
||||
import { WorkflowEditActionForm } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionForm';
|
||||
import { WorkflowEditActionForm } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionForm';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, within } from '@storybook/test';
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
@ -44,7 +44,7 @@ const DEFAULT_ACTION = {
|
||||
} satisfies WorkflowFormAction;
|
||||
|
||||
const meta: Meta<typeof WorkflowEditActionForm> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionForm',
|
||||
title: 'Modules/Workflow/Actions/Form/WorkflowEditActionForm',
|
||||
component: WorkflowEditActionForm,
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
@ -0,0 +1,90 @@
|
||||
import { WorkflowFormAction } from '@/workflow/types/Workflow';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
import { ComponentDecorator } from 'twenty-ui';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator';
|
||||
import { WorkflowEditActionFormFieldSettings } from '../WorkflowEditActionFormFieldSettings';
|
||||
|
||||
const meta: Meta<typeof WorkflowEditActionFormFieldSettings> = {
|
||||
title: 'Modules/Workflow/Actions/Form/WorkflowEditActionFormFieldSettings',
|
||||
component: WorkflowEditActionFormFieldSettings,
|
||||
decorators: [
|
||||
WorkflowStepActionDrawerDecorator,
|
||||
ComponentDecorator,
|
||||
I18nFrontDecorator,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof WorkflowEditActionFormFieldSettings>;
|
||||
|
||||
const mockAction: WorkflowFormAction = {
|
||||
id: 'form-action-1',
|
||||
type: 'FORM',
|
||||
name: 'Test Form',
|
||||
valid: true,
|
||||
settings: {
|
||||
input: [
|
||||
{
|
||||
id: 'field-1',
|
||||
label: 'Text Field',
|
||||
type: FieldMetadataType.TEXT,
|
||||
placeholder: 'Enter text',
|
||||
settings: {},
|
||||
},
|
||||
],
|
||||
outputSchema: {},
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: false },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const TextFieldSettings: Story = {
|
||||
args: {
|
||||
field: mockAction.settings.input[0],
|
||||
onClose: fn(),
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const typeSelect = await canvas.findByText('Text');
|
||||
expect(typeSelect).toBeVisible();
|
||||
|
||||
const placeholderInput = await canvas.findByText('Enter text');
|
||||
expect(placeholderInput).toBeVisible();
|
||||
|
||||
const closeButton = await canvas.findByRole('button');
|
||||
await userEvent.click(closeButton);
|
||||
expect(args.onClose).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const NumberFieldSettings: Story = {
|
||||
args: {
|
||||
field: {
|
||||
id: 'field-2',
|
||||
label: 'Number Field',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
placeholder: 'Enter number',
|
||||
settings: {},
|
||||
},
|
||||
onClose: fn(),
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const typeSelect = await canvas.findByText('Number');
|
||||
expect(typeSelect).toBeVisible();
|
||||
|
||||
const placeholderInput = await canvas.findByText('Enter number');
|
||||
expect(placeholderInput).toBeInTheDocument();
|
||||
|
||||
const closeButton = await canvas.findByRole('button');
|
||||
await userEvent.click(closeButton);
|
||||
expect(args.onClose).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
|
||||
export const getDefaultFormFieldSettings = (type: FieldMetadataType) => {
|
||||
switch (type) {
|
||||
case FieldMetadataType.TEXT:
|
||||
return {
|
||||
label: 'Text',
|
||||
placeholder: 'Enter your text',
|
||||
};
|
||||
case FieldMetadataType.NUMBER:
|
||||
return {
|
||||
label: 'Number',
|
||||
placeholder: '1000',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
label: type.charAt(0).toUpperCase() + type.slice(1),
|
||||
placeholder: 'Enter your value',
|
||||
};
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user