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

View File

@ -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>
);

View File

@ -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 />;
};

View File

@ -1,5 +1,5 @@
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 { 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 { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/mock-metadata-query-result';
import { mockedTasks } from '~/testing/mock-data/tasks';
import {
getWorkflowMock,
getWorkflowVersionsMock,
workflowQueryResult,
} from '~/testing/mock-data/workflow';
import { mockedRemoteServers } from './mock-data/remote-servers';
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', () => {
return HttpResponse.json({
data: {
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',
},
},
],
},
},
data: workflowQueryResult,
});
}),
graphql.query('FindOneWorkflow', () => {
return HttpResponse.json({
data: {
workflow: {
__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',
},
},
],
},
},
workflow: getWorkflowMock(),
},
});
}),
graphql.query('FindManyWorkflowVersions', () => {
return HttpResponse.json({
data: {
workflowVersions: {
__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',
},
},
},
],
},
workflowVersions: getWorkflowVersionsMock(),
},
});
}),

View File

@ -26,7 +26,7 @@ export const mockedEmptyPersonData = {
__typename: 'Person',
};
export const peopleQueryResult: { people: RecordGqlConnection } = {
export const peopleQueryResult = {
people: {
__typename: 'PersonConnection',
totalCount: 16,
@ -58,8 +58,8 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
email: 'asd.com',
name: {
__typename: 'FullName',
firstName: 'Test ',
lastName: 'tTest',
firstName: 'Test',
lastName: 'Test',
},
noteTargets: {
__typename: 'NoteTargetConnection',
@ -1719,4 +1719,4 @@ export const peopleQueryResult: { people: RecordGqlConnection } = {
},
],
},
};
} satisfies { people: RecordGqlConnection };

File diff suppressed because it is too large Load Diff