Disable the fields of all CRUD workflow actions on readonly mode (#9939)
Fixes https://discord.com/channels/1130383047699738754/1333822806504247467 In this PR: - Make the workflow step title input readonly when the visualizer is in readonly mode - Make all the fields of the Update Record and Delete Record readonly when the visualizer is in readonly mode - Create stories for the Create Record, Updated Record and Delete Record actions; I'm checking for the default mode and several variants of the disabled mode - Set up mocks for the workflows and use them in msw handlers Follow up: - We use `readonly` and `disabled` alternatively; these are two different states when talking about a HTML `<input />` element. I think we should settle on a single word. - Refactor the `<WorkflowSingleRecordPicker />` component to behave as other selects | Current component | Should look like | |--------|--------| |  |  |
This commit is contained in:
committed by
GitHub
parent
4e32fd1c98
commit
d946cdcba4
@ -27,6 +27,7 @@ type FormMultiSelectFieldInputProps = {
|
|||||||
VariablePicker?: VariablePickerComponent;
|
VariablePicker?: VariablePickerComponent;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
testId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledDisplayModeReadonlyContainer = styled.div`
|
const StyledDisplayModeReadonlyContainer = styled.div`
|
||||||
@ -68,6 +69,7 @@ export const FormMultiSelectFieldInput = ({
|
|||||||
VariablePicker,
|
VariablePicker,
|
||||||
readonly,
|
readonly,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
testId,
|
||||||
}: FormMultiSelectFieldInputProps) => {
|
}: FormMultiSelectFieldInputProps) => {
|
||||||
const inputId = useId();
|
const inputId = useId();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -176,7 +178,7 @@ export const FormMultiSelectFieldInput = ({
|
|||||||
const placeholderText = placeholder ?? label;
|
const placeholderText = placeholder ?? label;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormFieldInputContainer>
|
<FormFieldInputContainer data-testid={testId}>
|
||||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||||
|
|
||||||
<FormFieldInputRowContainer>
|
<FormFieldInputRowContainer>
|
||||||
|
|||||||
@ -49,12 +49,14 @@ export const WorkflowStepHeader = ({
|
|||||||
iconColor,
|
iconColor,
|
||||||
initialTitle,
|
initialTitle,
|
||||||
headerType,
|
headerType,
|
||||||
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
onTitleChange: (newTitle: string) => void;
|
onTitleChange: (newTitle: string) => void;
|
||||||
Icon: IconComponent;
|
Icon: IconComponent;
|
||||||
iconColor: string;
|
iconColor: string;
|
||||||
initialTitle: string;
|
initialTitle: string;
|
||||||
headerType: string;
|
headerType: string;
|
||||||
|
disabled?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [title, setTitle] = useState(initialTitle);
|
const [title, setTitle] = useState(initialTitle);
|
||||||
@ -67,17 +69,16 @@ export const WorkflowStepHeader = ({
|
|||||||
return (
|
return (
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
<StyledHeaderIconContainer>
|
<StyledHeaderIconContainer>
|
||||||
{
|
<Icon
|
||||||
<Icon
|
color={iconColor}
|
||||||
color={iconColor}
|
stroke={theme.icon.stroke.sm}
|
||||||
stroke={theme.icon.stroke.sm}
|
size={theme.icon.size.lg}
|
||||||
size={theme.icon.size.lg}
|
/>
|
||||||
/>
|
|
||||||
}
|
|
||||||
</StyledHeaderIconContainer>
|
</StyledHeaderIconContainer>
|
||||||
<StyledHeaderInfo>
|
<StyledHeaderInfo>
|
||||||
<StyledHeaderTitle>
|
<StyledHeaderTitle>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
disabled={disabled}
|
||||||
value={title}
|
value={title}
|
||||||
copyButton={false}
|
copyButton={false}
|
||||||
hotkeyScope="workflow-step-title"
|
hotkeyScope="workflow-step-title"
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||||
|
import { ComponentDecorator, IconPlus, THEME_LIGHT } from 'twenty-ui';
|
||||||
|
import { WorkflowStepHeader } from '../WorkflowStepHeader';
|
||||||
|
|
||||||
|
const meta: Meta<typeof WorkflowStepHeader> = {
|
||||||
|
title: 'Modules/Workflow/WorkflowStepHeader',
|
||||||
|
component: WorkflowStepHeader,
|
||||||
|
args: {
|
||||||
|
onTitleChange: fn(),
|
||||||
|
},
|
||||||
|
argTypes: {},
|
||||||
|
decorators: [ComponentDecorator],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof WorkflowStepHeader>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
headerType: 'Action',
|
||||||
|
iconColor: THEME_LIGHT.font.color.tertiary,
|
||||||
|
initialTitle: 'Create Record',
|
||||||
|
Icon: IconPlus,
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
expect(await canvas.findByDisplayValue('Create Record')).toBeVisible();
|
||||||
|
expect(await canvas.findByText('Action')).toBeVisible();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EditableTitle: Story = {
|
||||||
|
args: {
|
||||||
|
headerType: 'Action',
|
||||||
|
iconColor: THEME_LIGHT.font.color.tertiary,
|
||||||
|
initialTitle: 'Create Record',
|
||||||
|
Icon: IconPlus,
|
||||||
|
onTitleChange: fn(),
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement, args }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Create Record');
|
||||||
|
|
||||||
|
const NEW_TITLE = 'New Title';
|
||||||
|
|
||||||
|
await userEvent.clear(titleInput);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(args.onTitleChange).toHaveBeenCalledWith('');
|
||||||
|
});
|
||||||
|
|
||||||
|
await userEvent.type(titleInput, NEW_TITLE);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(args.onTitleChange).toHaveBeenCalledWith(NEW_TITLE);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(args.onTitleChange).toHaveBeenCalledTimes(2);
|
||||||
|
expect(titleInput).toHaveValue(NEW_TITLE);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled: Story = {
|
||||||
|
args: {
|
||||||
|
headerType: 'Action',
|
||||||
|
iconColor: THEME_LIGHT.font.color.tertiary,
|
||||||
|
initialTitle: 'Create Record',
|
||||||
|
Icon: IconPlus,
|
||||||
|
disabled: true,
|
||||||
|
onTitleChange: fn(),
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement, args }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Create Record');
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const NEW_TITLE = 'New Title';
|
||||||
|
|
||||||
|
await userEvent.type(titleInput, NEW_TITLE);
|
||||||
|
|
||||||
|
expect(args.onTitleChange).not.toHaveBeenCalled();
|
||||||
|
expect(titleInput).toHaveValue('Create Record');
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -177,6 +177,7 @@ export const WorkflowEditActionFormCreateRecord = ({
|
|||||||
iconColor={theme.font.color.tertiary}
|
iconColor={theme.font.color.tertiary}
|
||||||
initialTitle={headerTitle}
|
initialTitle={headerTitle}
|
||||||
headerType="Action"
|
headerType="Action"
|
||||||
|
disabled={isFormDisabled}
|
||||||
/>
|
/>
|
||||||
<WorkflowStepBody>
|
<WorkflowStepBody>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -125,6 +125,7 @@ export const WorkflowEditActionFormDeleteRecord = ({
|
|||||||
iconColor={theme.font.color.tertiary}
|
iconColor={theme.font.color.tertiary}
|
||||||
initialTitle={headerTitle}
|
initialTitle={headerTitle}
|
||||||
headerType="Action"
|
headerType="Action"
|
||||||
|
disabled={isFormDisabled}
|
||||||
/>
|
/>
|
||||||
<WorkflowStepBody>
|
<WorkflowStepBody>
|
||||||
<Select
|
<Select
|
||||||
@ -157,6 +158,8 @@ export const WorkflowEditActionFormDeleteRecord = ({
|
|||||||
}
|
}
|
||||||
objectNameSingular={formData.objectName}
|
objectNameSingular={formData.objectName}
|
||||||
defaultValue={formData.objectRecordId}
|
defaultValue={formData.objectRecordId}
|
||||||
|
testId="workflow-edit-action-record-delete-object-record-id"
|
||||||
|
disabled={isFormDisabled}
|
||||||
/>
|
/>
|
||||||
</WorkflowStepBody>
|
</WorkflowStepBody>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -177,6 +177,7 @@ export const WorkflowEditActionFormUpdateRecord = ({
|
|||||||
iconColor={theme.font.color.tertiary}
|
iconColor={theme.font.color.tertiary}
|
||||||
initialTitle={headerTitle}
|
initialTitle={headerTitle}
|
||||||
headerType="Action"
|
headerType="Action"
|
||||||
|
disabled={isFormDisabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WorkflowStepBody>
|
<WorkflowStepBody>
|
||||||
@ -205,15 +206,18 @@ export const WorkflowEditActionFormUpdateRecord = ({
|
|||||||
<HorizontalSeparator noMargin />
|
<HorizontalSeparator noMargin />
|
||||||
|
|
||||||
<WorkflowSingleRecordPicker
|
<WorkflowSingleRecordPicker
|
||||||
|
testId="workflow-edit-action-record-update-object-record-id"
|
||||||
label="Record"
|
label="Record"
|
||||||
onChange={(objectRecordId) =>
|
onChange={(objectRecordId) =>
|
||||||
handleFieldChange('objectRecordId', objectRecordId)
|
handleFieldChange('objectRecordId', objectRecordId)
|
||||||
}
|
}
|
||||||
objectNameSingular={formData.objectName}
|
objectNameSingular={formData.objectName}
|
||||||
defaultValue={formData.objectRecordId}
|
defaultValue={formData.objectRecordId}
|
||||||
|
disabled={isFormDisabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormMultiSelectFieldInput
|
<FormMultiSelectFieldInput
|
||||||
|
testId="workflow-edit-action-record-update-fields-to-update"
|
||||||
label="Fields to update"
|
label="Fields to update"
|
||||||
defaultValue={formData.fieldsToUpdate}
|
defaultValue={formData.fieldsToUpdate}
|
||||||
options={inlineFieldDefinitions.map((field) => ({
|
options={inlineFieldDefinitions.map((field) => ({
|
||||||
@ -226,6 +230,7 @@ export const WorkflowEditActionFormUpdateRecord = ({
|
|||||||
handleFieldChange('fieldsToUpdate', fieldsToUpdate)
|
handleFieldChange('fieldsToUpdate', fieldsToUpdate)
|
||||||
}
|
}
|
||||||
placeholder="Select fields to update"
|
placeholder="Select fields to update"
|
||||||
|
readonly={isFormDisabled}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalSeparator noMargin />
|
<HorizontalSeparator noMargin />
|
||||||
|
|||||||
@ -32,6 +32,7 @@ type WorkflowSingleRecordFieldChipProps = {
|
|||||||
selectedRecord?: ObjectRecord;
|
selectedRecord?: ObjectRecord;
|
||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
onRemove: () => void;
|
onRemove: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WorkflowSingleRecordFieldChip = ({
|
export const WorkflowSingleRecordFieldChip = ({
|
||||||
@ -39,6 +40,7 @@ export const WorkflowSingleRecordFieldChip = ({
|
|||||||
selectedRecord,
|
selectedRecord,
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
onRemove,
|
onRemove,
|
||||||
|
disabled,
|
||||||
}: WorkflowSingleRecordFieldChipProps) => {
|
}: WorkflowSingleRecordFieldChipProps) => {
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
|
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
|
||||||
|
|
||||||
@ -50,7 +52,7 @@ export const WorkflowSingleRecordFieldChip = ({
|
|||||||
return (
|
return (
|
||||||
<VariableChipStandalone
|
<VariableChipStandalone
|
||||||
rawVariableName={objectMetadataItem.labelSingular}
|
rawVariableName={objectMetadataItem.labelSingular}
|
||||||
onRemove={onRemove}
|
onRemove={disabled ? undefined : onRemove}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
|
|
||||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||||
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
||||||
|
import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer';
|
||||||
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
|
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
|
||||||
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
|
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
|
||||||
import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker';
|
import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker';
|
||||||
@ -24,18 +25,7 @@ import styled from '@emotion/styled';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { isValidUuid } from '~/utils/isValidUuid';
|
import { isValidUuid } from '~/utils/isValidUuid';
|
||||||
|
|
||||||
const StyledFormSelectContainer = styled.div`
|
const StyledFormSelectContainer = styled(FormFieldInputInputContainer)`
|
||||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
|
||||||
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
border-bottom-left-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
border-right: none;
|
|
||||||
border-bottom-right-radius: none;
|
|
||||||
border-top-right-radius: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
overflow: 'hidden';
|
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||||
@ -76,6 +66,8 @@ export type WorkflowSingleRecordPickerProps = {
|
|||||||
defaultValue: RecordId | Variable;
|
defaultValue: RecordId | Variable;
|
||||||
onChange: (value: RecordId | Variable) => void;
|
onChange: (value: RecordId | Variable) => void;
|
||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
testId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WorkflowSingleRecordPicker = ({
|
export const WorkflowSingleRecordPicker = ({
|
||||||
@ -83,6 +75,8 @@ export const WorkflowSingleRecordPicker = ({
|
|||||||
defaultValue,
|
defaultValue,
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
onChange,
|
onChange,
|
||||||
|
disabled,
|
||||||
|
testId,
|
||||||
}: WorkflowSingleRecordPickerProps) => {
|
}: WorkflowSingleRecordPickerProps) => {
|
||||||
const draftValue: WorkflowSingleRecordPickerValue =
|
const draftValue: WorkflowSingleRecordPickerValue =
|
||||||
isStandaloneVariableString(defaultValue)
|
isStandaloneVariableString(defaultValue)
|
||||||
@ -137,60 +131,65 @@ export const WorkflowSingleRecordPicker = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormFieldInputContainer>
|
<FormFieldInputContainer data-testid={testId}>
|
||||||
{label ? <InputLabel>{label}</InputLabel> : null}
|
{label ? <InputLabel>{label}</InputLabel> : null}
|
||||||
<FormFieldInputRowContainer>
|
<FormFieldInputRowContainer>
|
||||||
<StyledFormSelectContainer>
|
<StyledFormSelectContainer hasRightElement={!disabled}>
|
||||||
<WorkflowSingleRecordFieldChip
|
<WorkflowSingleRecordFieldChip
|
||||||
draftValue={draftValue}
|
draftValue={draftValue}
|
||||||
selectedRecord={selectedRecord}
|
selectedRecord={selectedRecord}
|
||||||
objectNameSingular={objectNameSingular}
|
objectNameSingular={objectNameSingular}
|
||||||
onRemove={handleUnlinkVariable}
|
onRemove={handleUnlinkVariable}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<DropdownScope dropdownScopeId={dropdownId}>
|
{!disabled && (
|
||||||
<Dropdown
|
<DropdownScope dropdownScopeId={dropdownId}>
|
||||||
dropdownId={dropdownId}
|
<Dropdown
|
||||||
dropdownPlacement="left-start"
|
dropdownId={dropdownId}
|
||||||
onClose={handleCloseRelationPickerDropdown}
|
dropdownPlacement="left-start"
|
||||||
clickableComponent={
|
onClose={handleCloseRelationPickerDropdown}
|
||||||
<LightIconButton
|
clickableComponent={
|
||||||
className="displayOnHover"
|
<LightIconButton
|
||||||
Icon={IconChevronDown}
|
className="displayOnHover"
|
||||||
accent="tertiary"
|
Icon={IconChevronDown}
|
||||||
/>
|
accent="tertiary"
|
||||||
}
|
|
||||||
dropdownComponents={
|
|
||||||
<RecordPickerComponentInstanceContext.Provider
|
|
||||||
value={{ instanceId: dropdownId }}
|
|
||||||
>
|
|
||||||
<SingleRecordSelect
|
|
||||||
EmptyIcon={IconForbid}
|
|
||||||
emptyLabel={'No ' + objectNameSingular}
|
|
||||||
onCancel={() => closeDropdown()}
|
|
||||||
onRecordSelected={handleRecordSelected}
|
|
||||||
objectNameSingular={objectNameSingular}
|
|
||||||
recordPickerInstanceId={dropdownId}
|
|
||||||
selectedRecordIds={
|
|
||||||
draftValue?.value &&
|
|
||||||
!isStandaloneVariableString(draftValue.value)
|
|
||||||
? [draftValue.value]
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</RecordPickerComponentInstanceContext.Provider>
|
}
|
||||||
}
|
dropdownComponents={
|
||||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
<RecordPickerComponentInstanceContext.Provider
|
||||||
/>
|
value={{ instanceId: dropdownId }}
|
||||||
</DropdownScope>
|
>
|
||||||
|
<SingleRecordSelect
|
||||||
|
EmptyIcon={IconForbid}
|
||||||
|
emptyLabel={'No ' + objectNameSingular}
|
||||||
|
onCancel={() => closeDropdown()}
|
||||||
|
onRecordSelected={handleRecordSelected}
|
||||||
|
objectNameSingular={objectNameSingular}
|
||||||
|
recordPickerInstanceId={dropdownId}
|
||||||
|
selectedRecordIds={
|
||||||
|
draftValue?.value &&
|
||||||
|
!isStandaloneVariableString(draftValue.value)
|
||||||
|
? [draftValue.value]
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</RecordPickerComponentInstanceContext.Provider>
|
||||||
|
}
|
||||||
|
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||||
|
/>
|
||||||
|
</DropdownScope>
|
||||||
|
)}
|
||||||
</StyledFormSelectContainer>
|
</StyledFormSelectContainer>
|
||||||
<StyledSearchVariablesDropdownContainer>
|
|
||||||
<WorkflowVariablesDropdown
|
{!disabled && (
|
||||||
inputId={variablesDropdownId}
|
<StyledSearchVariablesDropdownContainer>
|
||||||
onVariableSelect={handleVariableTagInsert}
|
<WorkflowVariablesDropdown
|
||||||
disabled={false}
|
inputId={variablesDropdownId}
|
||||||
objectNameSingularToSelect={objectNameSingular}
|
onVariableSelect={handleVariableTagInsert}
|
||||||
/>
|
objectNameSingularToSelect={objectNameSingular}
|
||||||
</StyledSearchVariablesDropdownContainer>
|
/>
|
||||||
|
</StyledSearchVariablesDropdownContainer>
|
||||||
|
)}
|
||||||
</FormFieldInputRowContainer>
|
</FormFieldInputRowContainer>
|
||||||
</FormFieldInputContainer>
|
</FormFieldInputContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { fn, userEvent, within } from '@storybook/test';
|
||||||
|
import { ComponentDecorator } from 'twenty-ui';
|
||||||
|
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||||
|
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||||
|
import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator';
|
||||||
|
import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator';
|
||||||
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow';
|
||||||
|
import { WorkflowEditActionFormCreateRecord } from '../WorkflowEditActionFormCreateRecord';
|
||||||
|
|
||||||
|
const meta: Meta<typeof WorkflowEditActionFormCreateRecord> = {
|
||||||
|
title: 'Modules/Workflow/WorkflowEditActionFormCreateRecord',
|
||||||
|
component: WorkflowEditActionFormCreateRecord,
|
||||||
|
parameters: {
|
||||||
|
msw: graphqlMocks,
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
id: getWorkflowNodeIdMock(),
|
||||||
|
name: 'Create Record',
|
||||||
|
type: 'CREATE_RECORD',
|
||||||
|
valid: false,
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
objectName: 'person',
|
||||||
|
objectRecord: {},
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
WorkflowStepActionDrawerDecorator,
|
||||||
|
WorkflowStepDecorator,
|
||||||
|
ComponentDecorator,
|
||||||
|
ObjectMetadataItemsDecorator,
|
||||||
|
SnackBarDecorator,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof WorkflowEditActionFormCreateRecord>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disabled: Story = {
|
||||||
|
args: {
|
||||||
|
actionOptions: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Create Record');
|
||||||
|
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const objectSelectCurrentValue = await canvas.findByText('People');
|
||||||
|
|
||||||
|
await userEvent.click(objectSelectCurrentValue);
|
||||||
|
|
||||||
|
const searchInputInSelectDropdown = canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,201 @@
|
|||||||
|
import { WorkflowDeleteRecordAction } from '@/workflow/types/Workflow';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||||
|
import { ComponentDecorator, RouterDecorator } from 'twenty-ui';
|
||||||
|
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||||
|
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||||
|
import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator';
|
||||||
|
import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator';
|
||||||
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getPeopleMock } from '~/testing/mock-data/people';
|
||||||
|
import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow';
|
||||||
|
import { WorkflowEditActionFormDeleteRecord } from '../WorkflowEditActionFormDeleteRecord';
|
||||||
|
|
||||||
|
const DEFAULT_ACTION = {
|
||||||
|
id: getWorkflowNodeIdMock(),
|
||||||
|
name: 'Delete Record',
|
||||||
|
type: 'DELETE_RECORD',
|
||||||
|
valid: false,
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
objectName: 'person',
|
||||||
|
objectRecordId: '',
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} satisfies WorkflowDeleteRecordAction;
|
||||||
|
|
||||||
|
const meta: Meta<typeof WorkflowEditActionFormDeleteRecord> = {
|
||||||
|
title: 'Modules/Workflow/WorkflowEditActionFormDeleteRecord',
|
||||||
|
component: WorkflowEditActionFormDeleteRecord,
|
||||||
|
parameters: {
|
||||||
|
msw: graphqlMocks,
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
action: DEFAULT_ACTION,
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
WorkflowStepActionDrawerDecorator,
|
||||||
|
WorkflowStepDecorator,
|
||||||
|
ComponentDecorator,
|
||||||
|
ObjectMetadataItemsDecorator,
|
||||||
|
SnackBarDecorator,
|
||||||
|
RouterDecorator,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof WorkflowEditActionFormDeleteRecord>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DisabledWithEmptyValues: Story = {
|
||||||
|
args: {
|
||||||
|
actionOptions: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Delete Record');
|
||||||
|
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const objectSelectCurrentValue = await canvas.findByText('People');
|
||||||
|
|
||||||
|
await userEvent.click(objectSelectCurrentValue);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
const openRecordSelectButton = within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-delete-object-record-id',
|
||||||
|
),
|
||||||
|
).queryByRole('button');
|
||||||
|
|
||||||
|
expect(openRecordSelectButton).not.toBeInTheDocument();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const peopleMock = getPeopleMock()[0];
|
||||||
|
|
||||||
|
export const DisabledWithDefaultStaticValues: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
...DEFAULT_ACTION,
|
||||||
|
settings: {
|
||||||
|
...DEFAULT_ACTION.settings,
|
||||||
|
input: {
|
||||||
|
...DEFAULT_ACTION.settings.input,
|
||||||
|
objectRecordId: peopleMock.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionOptions: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Delete Record');
|
||||||
|
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const objectSelectCurrentValue = await canvas.findByText('People');
|
||||||
|
|
||||||
|
await userEvent.click(objectSelectCurrentValue);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
const openRecordSelectButton = within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-delete-object-record-id',
|
||||||
|
),
|
||||||
|
).queryByRole('button');
|
||||||
|
|
||||||
|
expect(openRecordSelectButton).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
const selectedRecordToDelete = await canvas.findByText(
|
||||||
|
`${peopleMock.name.firstName} ${peopleMock.name.lastName}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(selectedRecordToDelete).toBeVisible();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DisabledWithDefaultVariableValues: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
...DEFAULT_ACTION,
|
||||||
|
settings: {
|
||||||
|
...DEFAULT_ACTION.settings,
|
||||||
|
input: {
|
||||||
|
...DEFAULT_ACTION.settings.input,
|
||||||
|
objectRecordId: '{{trigger.recordId}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionOptions: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Delete Record');
|
||||||
|
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const objectSelectCurrentValue = await canvas.findByText('People');
|
||||||
|
|
||||||
|
await userEvent.click(objectSelectCurrentValue);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
const openRecordSelectButton = within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-delete-object-record-id',
|
||||||
|
),
|
||||||
|
).queryByRole('button');
|
||||||
|
|
||||||
|
expect(openRecordSelectButton).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
const recordVariableToDelete = await within(
|
||||||
|
canvas.getByTestId('workflow-edit-action-record-delete-object-record-id'),
|
||||||
|
).findByText('Person');
|
||||||
|
|
||||||
|
expect(recordVariableToDelete).toBeVisible();
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,252 @@
|
|||||||
|
import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow';
|
||||||
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||||
|
import { ComponentDecorator, RouterDecorator } from 'twenty-ui';
|
||||||
|
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||||
|
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||||
|
import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator';
|
||||||
|
import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator';
|
||||||
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getPeopleMock } from '~/testing/mock-data/people';
|
||||||
|
import { getWorkflowNodeIdMock } from '~/testing/mock-data/workflow';
|
||||||
|
import { WorkflowEditActionFormUpdateRecord } from '../WorkflowEditActionFormUpdateRecord';
|
||||||
|
|
||||||
|
const DEFAULT_ACTION = {
|
||||||
|
id: getWorkflowNodeIdMock(),
|
||||||
|
name: 'Update Record',
|
||||||
|
type: 'UPDATE_RECORD',
|
||||||
|
settings: {
|
||||||
|
input: {
|
||||||
|
objectName: 'person',
|
||||||
|
objectRecordId: '',
|
||||||
|
objectRecord: {},
|
||||||
|
fieldsToUpdate: [
|
||||||
|
'updatedAt',
|
||||||
|
'averageEstimatedNumberOfAtomsInTheUniverse',
|
||||||
|
'comments',
|
||||||
|
'createdAt',
|
||||||
|
'deletedAt',
|
||||||
|
'name',
|
||||||
|
'participants',
|
||||||
|
'percentageOfCompletion',
|
||||||
|
'score',
|
||||||
|
'shortNotes',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
outputSchema: {},
|
||||||
|
errorHandlingOptions: {
|
||||||
|
retryOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
continueOnFailure: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
valid: false,
|
||||||
|
} satisfies WorkflowUpdateRecordAction;
|
||||||
|
|
||||||
|
const meta: Meta<typeof WorkflowEditActionFormUpdateRecord> = {
|
||||||
|
title: 'Modules/Workflow/WorkflowEditActionFormUpdateRecord',
|
||||||
|
component: WorkflowEditActionFormUpdateRecord,
|
||||||
|
parameters: {
|
||||||
|
msw: graphqlMocks,
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
action: DEFAULT_ACTION,
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
WorkflowStepActionDrawerDecorator,
|
||||||
|
WorkflowStepDecorator,
|
||||||
|
ComponentDecorator,
|
||||||
|
ObjectMetadataItemsDecorator,
|
||||||
|
SnackBarDecorator,
|
||||||
|
RouterDecorator,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof WorkflowEditActionFormUpdateRecord>;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
actionOptions: {
|
||||||
|
onActionUpdate: fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DisabledWithEmptyValues: Story = {
|
||||||
|
args: {
|
||||||
|
actionOptions: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Update Record');
|
||||||
|
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const objectSelectCurrentValue = await canvas.findByText('People');
|
||||||
|
|
||||||
|
await userEvent.click(objectSelectCurrentValue);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
const openRecordSelectButton = within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-update-object-record-id',
|
||||||
|
),
|
||||||
|
).queryByRole('button');
|
||||||
|
|
||||||
|
expect(openRecordSelectButton).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
const firstSelectedUpdatableField = await within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-update-fields-to-update',
|
||||||
|
),
|
||||||
|
).findByText('Creation date');
|
||||||
|
|
||||||
|
await userEvent.click(firstSelectedUpdatableField);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const peopleMock = getPeopleMock()[0];
|
||||||
|
|
||||||
|
export const DisabledWithDefaultStaticValues: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
...DEFAULT_ACTION,
|
||||||
|
settings: {
|
||||||
|
...DEFAULT_ACTION.settings,
|
||||||
|
input: {
|
||||||
|
...DEFAULT_ACTION.settings.input,
|
||||||
|
objectRecordId: peopleMock.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionOptions: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Update Record');
|
||||||
|
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const objectSelectCurrentValue = await canvas.findByText('People');
|
||||||
|
|
||||||
|
await userEvent.click(objectSelectCurrentValue);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedRecord = await canvas.findByText(
|
||||||
|
`${peopleMock.name.firstName} ${peopleMock.name.lastName}`,
|
||||||
|
);
|
||||||
|
expect(selectedRecord).toBeVisible();
|
||||||
|
|
||||||
|
const openRecordSelectButton = within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-update-object-record-id',
|
||||||
|
),
|
||||||
|
).queryByRole('button');
|
||||||
|
|
||||||
|
expect(openRecordSelectButton).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
const firstSelectedUpdatableField = await within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-update-fields-to-update',
|
||||||
|
),
|
||||||
|
).findByText('Creation date');
|
||||||
|
|
||||||
|
await userEvent.click(firstSelectedUpdatableField);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DisabledWithDefaultVariableValues: Story = {
|
||||||
|
args: {
|
||||||
|
action: {
|
||||||
|
...DEFAULT_ACTION,
|
||||||
|
settings: {
|
||||||
|
...DEFAULT_ACTION.settings,
|
||||||
|
input: {
|
||||||
|
...DEFAULT_ACTION.settings.input,
|
||||||
|
objectRecordId: '{{trigger.recordId}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionOptions: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
const titleInput = await canvas.findByDisplayValue('Update Record');
|
||||||
|
|
||||||
|
expect(titleInput).toBeDisabled();
|
||||||
|
|
||||||
|
const objectSelectCurrentValue = await canvas.findByText('People');
|
||||||
|
|
||||||
|
await userEvent.click(objectSelectCurrentValue);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
const openRecordSelectButton = within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-update-object-record-id',
|
||||||
|
),
|
||||||
|
).queryByRole('button');
|
||||||
|
|
||||||
|
expect(openRecordSelectButton).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
const firstSelectedUpdatableField = await within(
|
||||||
|
await canvas.findByTestId(
|
||||||
|
'workflow-edit-action-record-update-fields-to-update',
|
||||||
|
),
|
||||||
|
).findByText('Creation date');
|
||||||
|
|
||||||
|
await userEvent.click(firstSelectedUpdatableField);
|
||||||
|
|
||||||
|
{
|
||||||
|
const searchInputInSelectDropdown =
|
||||||
|
canvas.queryByPlaceholderText('Search');
|
||||||
|
|
||||||
|
expect(searchInputInSelectDropdown).not.toBeInTheDocument();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { Decorator } from '@storybook/react';
|
||||||
|
|
||||||
|
const StyledWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const WorkflowStepActionDrawerDecorator: Decorator = (Story) => (
|
||||||
|
<StyledWrapper>
|
||||||
|
<Story />
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||||
|
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
|
||||||
|
import { Decorator } from '@storybook/react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
import {
|
||||||
|
getWorkflowMock,
|
||||||
|
getWorkflowNodeIdMock,
|
||||||
|
} from '~/testing/mock-data/workflow';
|
||||||
|
|
||||||
|
export const WorkflowStepDecorator: Decorator = (Story) => {
|
||||||
|
const setWorkflowId = useSetRecoilState(workflowIdState);
|
||||||
|
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setWorkflowId(getWorkflowMock().id);
|
||||||
|
setWorkflowSelectedNode(getWorkflowNodeIdMock());
|
||||||
|
}, [setWorkflowId, setWorkflowSelectedNode]);
|
||||||
|
|
||||||
|
return <Story />;
|
||||||
|
};
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { getOperationName } from '@apollo/client/utilities';
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
import { graphql, http, HttpResponse } from 'msw';
|
import { graphql, GraphQLQuery, http, HttpResponse } from 'msw';
|
||||||
|
|
||||||
import { TRACK } from '@/analytics/graphql/queries/track';
|
import { TRACK } from '@/analytics/graphql/queries/track';
|
||||||
import { GET_CLIENT_CONFIG } from '@/client-config/graphql/queries/getClientConfig';
|
import { GET_CLIENT_CONFIG } from '@/client-config/graphql/queries/getClientConfig';
|
||||||
@ -23,6 +23,11 @@ import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
|
|||||||
import { GET_PUBLIC_WORKSPACE_DATA_BY_SUBDOMAIN } from '@/auth/graphql/queries/getPublicWorkspaceDataBySubdomain';
|
import { GET_PUBLIC_WORKSPACE_DATA_BY_SUBDOMAIN } from '@/auth/graphql/queries/getPublicWorkspaceDataBySubdomain';
|
||||||
import { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/mock-metadata-query-result';
|
import { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/mock-metadata-query-result';
|
||||||
import { mockedTasks } from '~/testing/mock-data/tasks';
|
import { mockedTasks } from '~/testing/mock-data/tasks';
|
||||||
|
import {
|
||||||
|
getWorkflowMock,
|
||||||
|
getWorkflowVersionsMock,
|
||||||
|
workflowQueryResult,
|
||||||
|
} from '~/testing/mock-data/workflow';
|
||||||
import { mockedRemoteServers } from './mock-data/remote-servers';
|
import { mockedRemoteServers } from './mock-data/remote-servers';
|
||||||
import { mockedViewFieldsData } from './mock-data/view-fields';
|
import { mockedViewFieldsData } from './mock-data/view-fields';
|
||||||
|
|
||||||
@ -638,135 +643,32 @@ export const graphqlMocks = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
graphql.query<GraphQLQuery, { objectRecordId: string }>(
|
||||||
|
'FindOnePerson',
|
||||||
|
({ variables: { objectRecordId } }) => {
|
||||||
|
return HttpResponse.json({
|
||||||
|
data: {
|
||||||
|
person: peopleMock.find((person) => person.id === objectRecordId),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
graphql.query('FindManyWorkflows', () => {
|
graphql.query('FindManyWorkflows', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: workflowQueryResult,
|
||||||
workflows: {
|
|
||||||
__typename: 'WorkflowConnection',
|
|
||||||
totalCount: 1,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
endCursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJpZCI6IjIwMGMxNTA4LWYxMDItNGJiOS1hZjMyLWVkYTU1MjM5YWU2MSJ9',
|
|
||||||
node: {
|
|
||||||
__typename: 'Workflow',
|
|
||||||
id: '200c1508-f102-4bb9-af32-eda55239ae61',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
graphql.query('FindOneWorkflow', () => {
|
graphql.query('FindOneWorkflow', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: {
|
||||||
workflow: {
|
workflow: getWorkflowMock(),
|
||||||
__typename: 'Workflow',
|
|
||||||
id: '200c1508-f102-4bb9-af32-eda55239ae61',
|
|
||||||
name: '1231 qqerrt',
|
|
||||||
statuses: null,
|
|
||||||
lastPublishedVersionId: '',
|
|
||||||
deletedAt: null,
|
|
||||||
updatedAt: '2024-09-19T10:10:04.505Z',
|
|
||||||
position: 0,
|
|
||||||
createdAt: '2024-09-19T10:10:04.505Z',
|
|
||||||
favorites: {
|
|
||||||
__typename: 'FavoriteConnection',
|
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
eventListeners: {
|
|
||||||
__typename: 'WorkflowEventListenerConnection',
|
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
runs: {
|
|
||||||
__typename: 'WorkflowRunConnection',
|
|
||||||
edges: [],
|
|
||||||
},
|
|
||||||
versions: {
|
|
||||||
__typename: 'WorkflowVersionConnection',
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowVersionEdge',
|
|
||||||
node: {
|
|
||||||
__typename: 'WorkflowVersion',
|
|
||||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
|
||||||
steps: null,
|
|
||||||
createdAt: '2024-09-19T10:10:04.725Z',
|
|
||||||
status: 'DRAFT',
|
|
||||||
name: 'v1',
|
|
||||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f0',
|
|
||||||
trigger: {
|
|
||||||
type: 'DATABASE_EVENT',
|
|
||||||
settings: {
|
|
||||||
eventName: 'note.created',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
deletedAt: null,
|
|
||||||
workflowId: '200c1508-f102-4bb9-af32-eda55239ae61',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
graphql.query('FindManyWorkflowVersions', () => {
|
graphql.query('FindManyWorkflowVersions', () => {
|
||||||
return HttpResponse.json({
|
return HttpResponse.json({
|
||||||
data: {
|
data: {
|
||||||
workflowVersions: {
|
workflowVersions: getWorkflowVersionsMock(),
|
||||||
__typename: 'WorkflowVersionConnection',
|
|
||||||
totalCount: 1,
|
|
||||||
pageInfo: {
|
|
||||||
__typename: 'PageInfo',
|
|
||||||
hasNextPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
startCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
endCursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
__typename: 'WorkflowVersionEdge',
|
|
||||||
cursor:
|
|
||||||
'eyJjcmVhdGVkQXQiOiIyMDI0LTA5LTE5VDEwOjEwOjA0LjcyNVoiLCJpZCI6ImY2MTg4NDNhLTI2YmUtNGE1NC1hNjBmLWY0Y2U4OGE1OTRmMCJ9',
|
|
||||||
node: {
|
|
||||||
__typename: 'WorkflowVersion',
|
|
||||||
updatedAt: '2024-09-19T10:13:12.075Z',
|
|
||||||
steps: null,
|
|
||||||
createdAt: '2024-09-19T10:10:04.725Z',
|
|
||||||
status: 'DRAFT',
|
|
||||||
name: 'v1',
|
|
||||||
id: 'f618843a-26be-4a54-a60f-f4ce88a594f0',
|
|
||||||
trigger: {
|
|
||||||
type: 'DATABASE_EVENT',
|
|
||||||
settings: {
|
|
||||||
eventName: 'note.created',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
deletedAt: null,
|
|
||||||
workflowId: '200c1508-f102-4bb9-af32-eda55239ae61',
|
|
||||||
workflow: {
|
|
||||||
__typename: 'Workflow',
|
|
||||||
id: '200c1508-f102-4bb9-af32-eda55239ae61',
|
|
||||||
name: '1231 qqerrt',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const mockedEmptyPersonData = {
|
|||||||
__typename: 'Person',
|
__typename: 'Person',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const peopleQueryResult: { people: RecordGqlConnection } = {
|
export const peopleQueryResult = {
|
||||||
people: {
|
people: {
|
||||||
__typename: 'PersonConnection',
|
__typename: 'PersonConnection',
|
||||||
totalCount: 16,
|
totalCount: 16,
|
||||||
@ -58,8 +58,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
|||||||
email: 'asd.com',
|
email: 'asd.com',
|
||||||
name: {
|
name: {
|
||||||
__typename: 'FullName',
|
__typename: 'FullName',
|
||||||
firstName: 'Test ',
|
firstName: 'Test',
|
||||||
lastName: 'tTest',
|
lastName: 'Test',
|
||||||
},
|
},
|
||||||
noteTargets: {
|
noteTargets: {
|
||||||
__typename: 'NoteTargetConnection',
|
__typename: 'NoteTargetConnection',
|
||||||
@ -1719,4 +1719,4 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
} satisfies { people: RecordGqlConnection };
|
||||||
|
|||||||
1466
packages/twenty-front/src/testing/mock-data/workflow.ts
Normal file
1466
packages/twenty-front/src/testing/mock-data/workflow.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user