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 |
|--------|--------|
| ![CleanShot 2025-01-30 at 17 30
29@2x](https://github.com/user-attachments/assets/104f2e7f-d758-4121-987a-f62f2e138df2)
| ![CleanShot 2025-01-30 at 17 30
49@2x](https://github.com/user-attachments/assets/e74b318e-a41a-40b9-9db8-bcc8015a1d67)
|
This commit is contained in:
Baptiste Devessier
2025-01-31 12:31:57 +01:00
committed by GitHub
parent 4e32fd1c98
commit d946cdcba4
16 changed files with 2227 additions and 186 deletions
@@ -49,12 +49,14 @@ export const WorkflowStepHeader = ({
iconColor,
initialTitle,
headerType,
disabled,
}: {
onTitleChange: (newTitle: string) => void;
Icon: IconComponent;
iconColor: string;
initialTitle: string;
headerType: string;
disabled?: boolean;
}) => {
const theme = useTheme();
const [title, setTitle] = useState(initialTitle);
@@ -67,17 +69,16 @@ export const WorkflowStepHeader = ({
return (
<StyledHeader>
<StyledHeaderIconContainer>
{
<Icon
color={iconColor}
stroke={theme.icon.stroke.sm}
size={theme.icon.size.lg}
/>
}
<Icon
color={iconColor}
stroke={theme.icon.stroke.sm}
size={theme.icon.size.lg}
/>
</StyledHeaderIconContainer>
<StyledHeaderInfo>
<StyledHeaderTitle>
<TextInput
disabled={disabled}
value={title}
copyButton={false}
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');
},
};