diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChip.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChip.tsx
index 7ba6392a3..07f615af5 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChip.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChip.tsx
@@ -9,7 +9,6 @@ const StyledChip = styled.div<{ deletable: boolean }>`
border-radius: 4px;
height: 20px;
box-sizing: border-box;
- cursor: pointer;
display: inline-flex;
align-items: center;
flex-direction: row;
@@ -20,10 +19,13 @@ const StyledChip = styled.div<{ deletable: boolean }>`
white-space: nowrap;
${({ theme, deletable }) =>
- !deletable &&
- css`
- padding-right: ${theme.spacing(1)};
- `}
+ !deletable
+ ? css`
+ padding-right: ${theme.spacing(1)};
+ `
+ : css`
+ cursor: pointer;
+ `}
`;
const StyledLabel = styled.span`
@@ -70,7 +72,7 @@ export const VariableChip = ({
{extractVariableLabel(rawVariableName)}
{onRemove ? (
-
+
) : null}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormRawJsonFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormRawJsonFieldInput.stories.tsx
index e334790be..21f1707ac 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormRawJsonFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormRawJsonFieldInput.stories.tsx
@@ -83,6 +83,45 @@ export const SaveValidJson: Story = {
},
};
+export const SaveValidMultilineJson: Story = {
+ args: {
+ placeholder: 'Enter valid json',
+ onPersist: fn(),
+ },
+ play: async ({ canvasElement, args }) => {
+ const editor = canvasElement.querySelector('.ProseMirror > p');
+ expect(editor).toBeVisible();
+
+ await userEvent.type(
+ editor,
+ '{{{Enter} "a": {{{Enter} "b" : "d"{Enter} }{Enter}}',
+ );
+
+ await waitFor(() => {
+ expect(args.onPersist).toHaveBeenCalledWith(
+ '{\n "a": {\n "b" : "d"\n }\n}',
+ );
+ });
+ },
+};
+
+export const MultilineWithDefaultValue: Story = {
+ args: {
+ placeholder: 'Enter valid json',
+ defaultValue: '{\n "a": {\n "b" : "d"\n }\n}',
+ },
+ play: async ({ canvasElement }) => {
+ const editor = canvasElement.querySelector('.ProseMirror > p');
+ expect(editor).toBeVisible();
+
+ await waitFor(() => {
+ expect((editor as HTMLElement).innerText).toBe(
+ '{\n "a": {\n "b" : "d"\n }\n}',
+ );
+ });
+ },
+};
+
export const DoesNotIgnoreInvalidJson: Story = {
args: {
placeholder: 'Enter valid json',
diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormTextFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormTextFieldInput.stories.tsx
index f0cb749bc..1b3352ed1 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormTextFieldInput.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormTextFieldInput.stories.tsx
@@ -1,5 +1,12 @@
import { Meta, StoryObj } from '@storybook/react';
-import { expect, fn, userEvent, within } from '@storybook/test';
+import {
+ expect,
+ fn,
+ userEvent,
+ waitFor,
+ waitForElementToBeRemoved,
+ within,
+} from '@storybook/test';
import { getUserDevice } from 'twenty-ui';
import { FormTextFieldInput } from '../FormTextFieldInput';
@@ -45,18 +52,92 @@ export const Multiline: Story = {
},
};
-export const WithVariablePicker: Story = {
+export const MultilineWithDefaultValue: Story = {
args: {
label: 'Text',
+ defaultValue: 'Line 1\nLine 2\n\nLine 4',
placeholder: 'Text field...',
- VariablePicker: () => VariablePicker
,
+ multiline: true,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
- const variablePicker = await canvas.findByText('VariablePicker');
+ await canvas.findByText(/^Text$/);
- expect(variablePicker).toBeVisible();
+ const editor = canvasElement.querySelector('.ProseMirror > p');
+
+ expect((editor as HTMLElement).innerText).toBe('Line 1\nLine 2\n\nLine 4');
+ },
+};
+
+export const WithVariable: Story = {
+ args: {
+ label: 'Text',
+ placeholder: 'Text field...',
+ VariablePicker: ({ onVariableSelect }) => {
+ return (
+
+ );
+ },
+ onPersist: fn(),
+ },
+ play: async ({ canvasElement, args }) => {
+ const canvas = within(canvasElement);
+
+ const addVariableButton = await canvas.findByRole('button', {
+ name: 'Add variable',
+ });
+
+ await userEvent.click(addVariableButton);
+
+ const variable = await canvas.findByText('test');
+ expect(variable).toBeVisible();
+
+ await waitFor(() => {
+ expect(args.onPersist).toHaveBeenCalledWith('{{test}}');
+ });
+ expect(args.onPersist).toHaveBeenCalledTimes(1);
+ },
+};
+
+export const WithDeletableVariable: Story = {
+ args: {
+ label: 'Text',
+ placeholder: 'Text field...',
+ defaultValue: 'test {{a.b.variable}} test',
+ onPersist: fn(),
+ },
+ play: async ({ canvasElement, args }) => {
+ const canvas = within(canvasElement);
+
+ const editor = canvasElement.querySelector('.ProseMirror > p');
+ expect(editor).toBeVisible();
+
+ const variable = await canvas.findByText('variable');
+ expect(variable).toBeVisible();
+
+ const deleteVariableButton = await canvas.findByRole('button', {
+ name: 'Remove variable',
+ });
+
+ await Promise.all([
+ waitForElementToBeRemoved(variable),
+
+ deleteVariableButton.click(),
+ ]);
+
+ expect(editor).toHaveTextContent('test test');
+
+ await waitFor(() => {
+ expect(args.onPersist).toHaveBeenCalledWith('test test');
+ });
+ expect(args.onPersist).toHaveBeenCalledTimes(1);
},
};
@@ -89,6 +170,28 @@ export const Disabled: Story = {
},
};
+export const DisabledWithVariable: Story = {
+ args: {
+ label: 'Text',
+ defaultValue: 'test {{a.b.variable}} test',
+ readonly: true,
+ },
+ play: async ({ canvasElement }) => {
+ const editor = canvasElement.querySelector('.ProseMirror > p');
+
+ expect(editor).toBeVisible();
+
+ await waitFor(() => {
+ expect(editor).toHaveTextContent('test variable test');
+ });
+
+ const deleteVariableButton = within(editor as HTMLElement).queryByRole(
+ 'button',
+ );
+ expect(deleteVariableButton).not.toBeInTheDocument();
+ },
+};
+
export const HasHistory: Story = {
args: {
label: 'Text',
diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/hooks/useTextVariableEditor.ts b/packages/twenty-front/src/modules/object-record/record-field/form-types/hooks/useTextVariableEditor.ts
index 232454437..d3125b5f2 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/form-types/hooks/useTextVariableEditor.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/hooks/useTextVariableEditor.ts
@@ -1,4 +1,4 @@
-import { initializeEditorContent } from '@/workflow/workflow-variables/utils/initializeEditorContent';
+import { getInitialEditorContent } from '@/workflow/workflow-variables/utils/getInitialEditorContent';
import { VariableTag } from '@/workflow/workflow-variables/utils/variableTag';
import Document from '@tiptap/extension-document';
import HardBreak from '@tiptap/extension-hard-break';
@@ -7,7 +7,6 @@ import Paragraph from '@tiptap/extension-paragraph';
import { default as Placeholder } from '@tiptap/extension-placeholder';
import Text from '@tiptap/extension-text';
import { Editor, useEditor } from '@tiptap/react';
-import { useState } from 'react';
import { isDefined } from 'twenty-ui';
type UseTextVariableEditorProps = {
@@ -25,8 +24,6 @@ export const useTextVariableEditor = ({
defaultValue,
onUpdate,
}: UseTextVariableEditorProps) => {
- const [isInitializing, setIsInitializing] = useState(true);
-
const editor = useEditor({
extensions: [
Document,
@@ -45,17 +42,11 @@ export const useTextVariableEditor = ({
: []),
History,
],
+ content: isDefined(defaultValue)
+ ? getInitialEditorContent(defaultValue)
+ : undefined,
editable: !readonly,
- onCreate: ({ editor }) => {
- if (isDefined(defaultValue)) {
- initializeEditorContent(editor, defaultValue);
- }
- setIsInitializing(false);
- },
onUpdate: ({ editor }) => {
- if (isInitializing) {
- return;
- }
onUpdate(editor);
},
editorProps: {
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowTextEditorVariableChip.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowTextEditorVariableChip.tsx
index 885f9f5dd..bf4fa8a27 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowTextEditorVariableChip.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowTextEditorVariableChip.tsx
@@ -12,6 +12,7 @@ type WorkflowTextEditorVariableChipProps = NodeViewProps;
export const WorkflowTextEditorVariableChip = ({
deleteNode,
node,
+ editor,
}: WorkflowTextEditorVariableChipProps) => {
const attrs = node.attrs as {
variable: string;
@@ -19,7 +20,10 @@ export const WorkflowTextEditorVariableChip = ({
return (
-
+
);
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getInitialEditorContent.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getInitialEditorContent.test.ts
new file mode 100644
index 000000000..88000ea27
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getInitialEditorContent.test.ts
@@ -0,0 +1,306 @@
+import { getInitialEditorContent } from '../getInitialEditorContent';
+
+describe('getInitialEditorContent', () => {
+ it('should handle single line text', () => {
+ expect(getInitialEditorContent('Hello world')).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Hello world",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle text with newlines', () => {
+ expect(getInitialEditorContent('Line 1\nLine 2')).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Line 1",
+ "type": "text",
+ },
+ {
+ "type": "hardBreak",
+ },
+ {
+ "text": "Line 2",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle single variable', () => {
+ expect(getInitialEditorContent('{{user.name}}')).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "attrs": {
+ "variable": "{{user.name}}",
+ },
+ "type": "variableTag",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle text with variables', () => {
+ expect(getInitialEditorContent('Hello {{user.name}}, welcome!'))
+ .toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Hello ",
+ "type": "text",
+ },
+ {
+ "attrs": {
+ "variable": "{{user.name}}",
+ },
+ "type": "variableTag",
+ },
+ {
+ "text": ", welcome!",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle text with multiple variables', () => {
+ expect(
+ getInitialEditorContent('Hello {{user.firstName}} {{user.lastName}}!'),
+ ).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Hello ",
+ "type": "text",
+ },
+ {
+ "attrs": {
+ "variable": "{{user.firstName}}",
+ },
+ "type": "variableTag",
+ },
+ {
+ "text": " ",
+ "type": "text",
+ },
+ {
+ "attrs": {
+ "variable": "{{user.lastName}}",
+ },
+ "type": "variableTag",
+ },
+ {
+ "text": "!",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle newlines with variables', () => {
+ expect(
+ getInitialEditorContent('Hello {{user.name}}\nWelcome to {{app.name}}'),
+ ).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Hello ",
+ "type": "text",
+ },
+ {
+ "attrs": {
+ "variable": "{{user.name}}",
+ },
+ "type": "variableTag",
+ },
+ {
+ "type": "hardBreak",
+ },
+ {
+ "text": "Welcome to ",
+ "type": "text",
+ },
+ {
+ "attrs": {
+ "variable": "{{app.name}}",
+ },
+ "type": "variableTag",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle empty strings', () => {
+ expect(getInitialEditorContent('')).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle multiple empty parts', () => {
+ expect(getInitialEditorContent('Hello {{user.name}} !'))
+ .toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Hello ",
+ "type": "text",
+ },
+ {
+ "attrs": {
+ "variable": "{{user.name}}",
+ },
+ "type": "variableTag",
+ },
+ {
+ "text": " !",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle multiple newlines', () => {
+ expect(getInitialEditorContent('Line1\n\nLine3')).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Line1",
+ "type": "text",
+ },
+ {
+ "type": "hardBreak",
+ },
+ {
+ "type": "hardBreak",
+ },
+ {
+ "text": "Line3",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should ignore malformed variable tags', () => {
+ expect(
+ getInitialEditorContent('Hello {{user.name}} and {{invalid}more}} text'),
+ ).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Hello ",
+ "type": "text",
+ },
+ {
+ "attrs": {
+ "variable": "{{user.name}}",
+ },
+ "type": "variableTag",
+ },
+ {
+ "text": " and {{invalid}more}} text",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+
+ it('should handle trailing newlines', () => {
+ expect(getInitialEditorContent('Hello\n')).toMatchInlineSnapshot(`
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "Hello",
+ "type": "text",
+ },
+ {
+ "type": "hardBreak",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`);
+ });
+});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/initializeEditorContent.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/initializeEditorContent.test.ts
deleted file mode 100644
index e9056342c..000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/initializeEditorContent.test.ts
+++ /dev/null
@@ -1,195 +0,0 @@
-import { Editor } from '@tiptap/react';
-import { initializeEditorContent } from '../initializeEditorContent';
-
-describe('initializeEditorContent', () => {
- let mockEditor: Editor;
-
- beforeEach(() => {
- mockEditor = {
- commands: {
- insertContent: jest.fn(),
- },
- } as unknown as Editor;
- });
-
- it('should handle single line text', () => {
- initializeEditorContent(mockEditor, 'Hello world');
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(1);
- expect(mockEditor.commands.insertContent).toHaveBeenCalledWith(
- 'Hello world',
- );
- });
-
- it('should handle text with newlines', () => {
- initializeEditorContent(mockEditor, 'Line 1\nLine 2');
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(3);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Line 1',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'hardBreak',
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 3,
- 'Line 2',
- );
- });
-
- it('should handle single variable', () => {
- initializeEditorContent(mockEditor, '{{user.name}}');
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(1);
- expect(mockEditor.commands.insertContent).toHaveBeenCalledWith({
- type: 'variableTag',
- attrs: { variable: '{{user.name}}' },
- });
- });
-
- it('should handle text with variables', () => {
- initializeEditorContent(mockEditor, 'Hello {{user.name}}, welcome!');
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(3);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Hello ',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'variableTag',
- attrs: { variable: '{{user.name}}' },
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 3,
- ', welcome!',
- );
- });
-
- it('should handle text with multiple variables', () => {
- initializeEditorContent(
- mockEditor,
- 'Hello {{user.firstName}} {{user.lastName}}!',
- );
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(5);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Hello ',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'variableTag',
- attrs: { variable: '{{user.firstName}}' },
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(3, ' ');
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(4, {
- type: 'variableTag',
- attrs: { variable: '{{user.lastName}}' },
- });
- });
-
- it('should handle newlines with variables', () => {
- initializeEditorContent(
- mockEditor,
- 'Hello {{user.name}}\nWelcome to {{app.name}}',
- );
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(5);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Hello ',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'variableTag',
- attrs: { variable: '{{user.name}}' },
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(3, {
- type: 'hardBreak',
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 4,
- 'Welcome to ',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(5, {
- type: 'variableTag',
- attrs: { variable: '{{app.name}}' },
- });
- });
-
- it('should handle empty strings', () => {
- initializeEditorContent(mockEditor, '');
- expect(mockEditor.commands.insertContent).not.toHaveBeenCalled();
- });
-
- it('should handle multiple empty parts', () => {
- initializeEditorContent(mockEditor, 'Hello {{user.name}} !');
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(3);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Hello ',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'variableTag',
- attrs: { variable: '{{user.name}}' },
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 3,
- ' !',
- );
- });
-
- it('should handle multiple newlines', () => {
- initializeEditorContent(mockEditor, 'Line1\n\nLine3');
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(4);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Line1',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'hardBreak',
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(3, {
- type: 'hardBreak',
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 4,
- 'Line3',
- );
- });
-
- it('should ignore malformed variable tags', () => {
- initializeEditorContent(
- mockEditor,
- 'Hello {{user.name}} and {{invalid}more}} text',
- );
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(3);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Hello ',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'variableTag',
- attrs: { variable: '{{user.name}}' },
- });
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 3,
- ' and {{invalid}more}} text',
- );
- });
-
- it('should handle trailing newlines', () => {
- initializeEditorContent(mockEditor, 'Hello\n');
-
- expect(mockEditor.commands.insertContent).toHaveBeenCalledTimes(2);
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(
- 1,
- 'Hello',
- );
- expect(mockEditor.commands.insertContent).toHaveBeenNthCalledWith(2, {
- type: 'hardBreak',
- });
- });
-});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getInitialEditorContent.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getInitialEditorContent.ts
new file mode 100644
index 000000000..6e4a5475e
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/getInitialEditorContent.ts
@@ -0,0 +1,44 @@
+import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
+import { isNonEmptyString } from '@sniptt/guards';
+import { JSONContent } from '@tiptap/react';
+
+export const CAPTURE_VARIABLE_TAG_REGEX = /({{[^{}]+}})/;
+
+export const getInitialEditorContent = (rawContent: string): JSONContent => {
+ const paragraphContent: JSONContent[] = [];
+ const lines = rawContent.split(/\n/);
+
+ lines.forEach((line, index) => {
+ const parts = line.split(CAPTURE_VARIABLE_TAG_REGEX);
+
+ parts.forEach((part) => {
+ if (isStandaloneVariableString(part)) {
+ paragraphContent.push({
+ type: 'variableTag',
+ attrs: { variable: part },
+ });
+ } else if (isNonEmptyString(part)) {
+ paragraphContent.push({
+ type: 'text',
+ text: part,
+ });
+ }
+ });
+
+ if (index < lines.length - 1) {
+ paragraphContent.push({
+ type: 'hardBreak',
+ });
+ }
+ });
+
+ return {
+ type: 'doc',
+ content: [
+ {
+ type: 'paragraph',
+ content: paragraphContent,
+ },
+ ],
+ };
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/initializeEditorContent.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/initializeEditorContent.ts
deleted file mode 100644
index a194de4ca..000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/initializeEditorContent.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { isNonEmptyString } from '@sniptt/guards';
-import { Editor } from '@tiptap/react';
-
-export const CAPTURE_VARIABLE_TAG_REGEX = /({{[^{}]+}})/;
-
-export const initializeEditorContent = (editor: Editor, content: string) => {
- const lines = content.split(/\n/);
-
- lines.forEach((line, index) => {
- const parts = line.split(CAPTURE_VARIABLE_TAG_REGEX);
- parts.forEach((part) => {
- if (part.length === 0) {
- return;
- }
-
- if (part.startsWith('{{') && part.endsWith('}}')) {
- editor.commands.insertContent({
- type: 'variableTag',
- attrs: { variable: part },
- });
- return;
- }
-
- if (isNonEmptyString(part)) {
- editor.commands.insertContent(part);
- }
- });
-
- // Add hard break if it's not the last line
- if (index < lines.length - 1) {
- editor.commands.insertContent({
- type: 'hardBreak',
- });
- }
- });
-};