Skip to content

Commit

Permalink
Improve the design of workflow nodes (#9810)
Browse files Browse the repository at this point in the history
- Go over every node in the workflows and fix the styles to conform to
Figma
- Create stories for every node type
  • Loading branch information
Devessier authored Jan 23, 2025
1 parent 337b6a8 commit bbb0c9a
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { NODE_BORDER_WIDTH } from '@/workflow/workflow-diagram/constants/NodeBorderWidth';
import { NODE_HANDLE_HEIGHT_PX } from '@/workflow/workflow-diagram/constants/NodeHandleHeightPx';
import { NODE_HANDLE_WIDTH_PX } from '@/workflow/workflow-diagram/constants/NodeHandleWidthPx';
import { NODE_ICON_LEFT_MARGIN } from '@/workflow/workflow-diagram/constants/NodeIconLeftMargin';
import { NODE_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/NodeIconWidth';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import styled from '@emotion/styled';
import { Handle, Position } from '@xyflow/react';
Expand All @@ -21,7 +26,7 @@ const StyledStepNodeType = styled.div`
${({ theme }) => theme.border.radius.sm} 0 0;
color: ${({ theme }) => theme.font.color.light};
font-size: ${({ theme }) => theme.font.size.md};
font-size: 9px;
font-weight: ${({ theme }) => theme.font.weight.semiBold};
margin-left: ${({ theme }) => theme.spacing(2)};
Expand All @@ -38,9 +43,8 @@ const StyledStepNodeType = styled.div`

const StyledStepNodeInnerContainer = styled.div<{ variant?: Variant }>`
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-style: ${({ variant }) =>
variant === 'placeholder' ? 'dashed' : null};
border: ${NODE_BORDER_WIDTH}px solid
${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
Expand All @@ -61,7 +65,7 @@ const StyledStepNodeInnerContainer = styled.div<{ variant?: Variant }>`
const StyledStepNodeLabel = styled.div<{ variant?: Variant }>`
align-items: center;
display: flex;
font-size: ${({ theme }) => theme.font.size.lg};
font-size: 13px;
font-weight: ${({ theme }) => theme.font.weight.medium};
column-gap: ${({ theme }) => theme.spacing(2)};
color: ${({ variant, theme }) =>
Expand All @@ -71,24 +75,27 @@ const StyledStepNodeLabel = styled.div<{ variant?: Variant }>`
max-width: 200px;
`;

const StyledSourceHandle = styled(Handle)`
export const StyledHandle = styled(Handle)`
background-color: ${({ theme }) => theme.grayScale.gray25};
border: none;
width: 4px;
height: 4px;
left: ${({ theme }) => theme.spacing(10)};
width: ${NODE_HANDLE_WIDTH_PX}px;
height: ${NODE_HANDLE_HEIGHT_PX}px;
`;

export const StyledTargetHandle = styled(Handle)`
left: ${({ theme }) => theme.spacing(10)};
const StyledSourceHandle = styled(StyledHandle)`
left: ${NODE_ICON_WIDTH + NODE_ICON_LEFT_MARGIN + NODE_BORDER_WIDTH}px;
`;

const StyledTargetHandle = styled(StyledSourceHandle)`
left: ${NODE_ICON_WIDTH + NODE_ICON_LEFT_MARGIN + NODE_BORDER_WIDTH}px;
visibility: hidden;
`;

const StyledRightFloatingElementContainer = styled.div`
display: flex;
align-items: center;
position: absolute;
right: ${({ theme }) => theme.spacing(-3)};
right: ${({ theme }) => theme.spacing(-4)};
bottom: 0;
top: 0;
transform: translateX(100%);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { StyledHandle } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode';
import { NODE_BORDER_WIDTH } from '@/workflow/workflow-diagram/constants/NodeBorderWidth';
import { NODE_ICON_LEFT_MARGIN } from '@/workflow/workflow-diagram/constants/NodeIconLeftMargin';
import { NODE_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/NodeIconWidth';
import styled from '@emotion/styled';
import { Handle, Position } from '@xyflow/react';
import { Position } from '@xyflow/react';
import { IconButton, IconPlus } from 'twenty-ui';

const StyledContainer = styled.div`
padding-left: ${({ theme }) => theme.spacing(6)};
padding-top: ${({ theme }) => theme.spacing(1)};
transform: translateX(-50%);
position: relative;
left: ${NODE_ICON_WIDTH + NODE_ICON_LEFT_MARGIN + NODE_BORDER_WIDTH}px;
`;

export const StyledTargetHandle = styled(Handle)`
left: ${({ theme }) => theme.spacing(10)};
const StyledTargetHandle = styled(StyledHandle)`
visibility: hidden;
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export const WorkflowDiagramEmptyTrigger = () => {
variant="placeholder"
Icon={
<StyledStepNodeLabelIconContainer>
<IconPlaylistAdd size={16} color={theme.font.color.tertiary} />
<IconPlaylistAdd
size={theme.icon.size.md}
color={theme.font.color.tertiary}
/>
</StyledStepNodeLabelIconContainer>
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const WorkflowDiagramStepNodeBase = ({
return (
<StyledStepNodeLabelIconContainer>
<Icon
size={theme.icon.size.lg}
size={theme.icon.size.md}
color={theme.font.color.tertiary}
/>
</StyledStepNodeLabelIconContainer>
Expand All @@ -43,7 +43,7 @@ export const WorkflowDiagramStepNodeBase = ({
return (
<StyledStepNodeLabelIconContainer>
<Icon
size={theme.icon.size.lg}
size={theme.icon.size.md}
color={theme.font.color.tertiary}
/>
</StyledStepNodeLabelIconContainer>
Expand All @@ -58,14 +58,18 @@ export const WorkflowDiagramStepNodeBase = ({
case 'CODE': {
return (
<StyledStepNodeLabelIconContainer>
<Icon size={theme.icon.size.lg} color={theme.color.orange} />
<Icon
size={theme.icon.size.md}
color={theme.color.orange}
stroke={theme.icon.stroke.sm}
/>
</StyledStepNodeLabelIconContainer>
);
}
case 'SEND_EMAIL': {
return (
<StyledStepNodeLabelIconContainer>
<Icon size={theme.icon.size.lg} color={theme.color.blue} />
<Icon size={theme.icon.size.md} color={theme.color.blue} />
</StyledStepNodeLabelIconContainer>
);
}
Expand All @@ -75,7 +79,7 @@ export const WorkflowDiagramStepNodeBase = ({
return (
<StyledStepNodeLabelIconContainer>
<Icon
size={theme.icon.size.lg}
size={theme.icon.size.md}
color={theme.font.color.tertiary}
stroke={theme.icon.stroke.sm}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { workflowIdState } from '@/workflow/states/workflowIdState';
import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined';
import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase';
import { WorkflowDiagramStepNodeEditableContent } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep';
import { useRecoilValue } from 'recoil';
import { FloatingIconButton, IconTrash } from 'twenty-ui';

export const WorkflowDiagramStepNodeEditable = ({
id,
Expand All @@ -26,19 +25,12 @@ export const WorkflowDiagramStepNodeEditable = ({
});

return (
<WorkflowDiagramStepNodeBase
<WorkflowDiagramStepNodeEditableContent
data={data}
RightFloatingElement={
selected ? (
<FloatingIconButton
size="medium"
Icon={IconTrash}
onClick={() => {
deleteStep(id);
}}
/>
) : undefined
}
selected={selected ?? false}
onDelete={() => {
deleteStep(id);
}}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { FloatingIconButton, IconTrash } from 'twenty-ui';

export const WorkflowDiagramStepNodeEditableContent = ({
data,
selected,
onDelete,
}: {
data: WorkflowDiagramStepNodeData;
selected: boolean;
onDelete: () => void;
}) => {
return (
<WorkflowDiagramStepNodeBase
data={data}
RightFloatingElement={
selected ? (
<FloatingIconButton
size="medium"
Icon={IconTrash}
onClick={onDelete}
/>
) : undefined
}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Meta, StoryObj } from '@storybook/react';

import { ReactFlowProvider } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { ComponentDecorator } from 'twenty-ui';
import { WorkflowDiagramCreateStepNode } from '../WorkflowDiagramCreateStepNode';

const meta: Meta<typeof WorkflowDiagramCreateStepNode> = {
title: 'Modules/Workflow/WorkflowDiagramCreateStepNode',
component: WorkflowDiagramCreateStepNode,
decorators: [
ComponentDecorator,
(Story) => (
<ReactFlowProvider>
<Story />
</ReactFlowProvider>
),
],
};

export default meta;
type Story = StoryObj<typeof WorkflowDiagramCreateStepNode>;

export const Default: Story = {
decorators: [
(Story) => (
<div style={{ position: 'relative' }}>
<Story />
</div>
),
],
};

export const Selected: Story = {
decorators: [
(Story) => (
<div className="selectable selected" style={{ position: 'relative' }}>
<Story />
</div>
),
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';

import { ReactFlowProvider } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { WorkflowDiagramEmptyTrigger } from '../WorkflowDiagramEmptyTrigger';

const meta: Meta<typeof WorkflowDiagramEmptyTrigger> = {
title: 'Modules/Workflow/WorkflowDiagramEmptyTrigger',
component: WorkflowDiagramEmptyTrigger,
};

export default meta;
type Story = StoryObj<typeof WorkflowDiagramEmptyTrigger>;

export const Default: Story = {
decorators: [
(Story) => (
<ReactFlowProvider>
<div style={{ position: 'relative' }}>
<Story />
</div>
</ReactFlowProvider>
),
ComponentDecorator,
],
};

export const Selected: Story = {
decorators: [
(Story) => (
<div className="selectable selected" style={{ position: 'relative' }}>
<Story />
</div>
),
(Story) => (
<ReactFlowProvider>
<Story />
</ReactFlowProvider>
),
ComponentDecorator,
],
};
Loading

0 comments on commit bbb0c9a

Please sign in to comment.