From 1e9dce3fd56af59ed776c87b2d2907e6f600ce5d Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Tue, 28 Jan 2025 14:59:45 +0100 Subject: [PATCH] Fix the design of the Variable chip (#9871) - Use a React component for variable tags in tiptap - Fix the design of the variable chip - Always display a button to delete the chip ![CleanShot 2025-01-28 at 12 35 55@2x](https://github.com/user-attachments/assets/d78ffa52-fcc3-4bbc-b427-68edde255408) --- .../components/FormBooleanFieldInput.tsx | 4 +- .../components/FormDateTimeFieldInput.tsx | 4 +- .../components/FormMultiSelectFieldInput.tsx | 4 +- .../components/FormNumberFieldInput.tsx | 4 +- .../components/FormSelectFieldInput.tsx | 4 +- .../components/FormUuidFieldInput.tsx | 4 +- .../form-types/components/VariableChip.tsx | 47 ++++++++----------- .../components/VariableChipStandalone.tsx | 24 ++++++++++ .../FormRawJsonFieldInput.stories.tsx | 10 +--- .../WorkflowSingleRecordFieldChip.tsx | 4 +- .../WorkflowTextEditorVariableChip.tsx | 25 ++++++++++ .../workflow-variables/utils/variableTag.ts | 7 ++- 12 files changed, 91 insertions(+), 50 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChipStandalone.tsx create mode 100644 packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowTextEditorVariableChip.tsx diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormBooleanFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormBooleanFieldInput.tsx index 3d170772d..0176248f4 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormBooleanFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormBooleanFieldInput.tsx @@ -1,7 +1,7 @@ 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 { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; import { BooleanInput } from '@/ui/field/input/components/BooleanInput'; import { InputLabel } from '@/ui/input/components/InputLabel'; @@ -96,7 +96,7 @@ export const FormBooleanFieldInput = ({ /> ) : ( - diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormDateTimeFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormDateTimeFieldInput.tsx index 9fd25f7f5..a3ea73e77 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormDateTimeFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormDateTimeFieldInput.tsx @@ -1,7 +1,7 @@ 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 { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; import { InputLabel } from '@/ui/input/components/InputLabel'; import { @@ -367,7 +367,7 @@ export const FormDateTimeFieldInput = ({ ) : null} ) : ( - diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormMultiSelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormMultiSelectFieldInput.tsx index ac9c54c2f..2646c8725 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormMultiSelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormMultiSelectFieldInput.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled'; 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 { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata'; import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId'; @@ -221,7 +221,7 @@ export const FormMultiSelectFieldInput = ({ ) ) : ( - diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormNumberFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormNumberFieldInput.tsx index a641c214d..cdc01e619 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormNumberFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormNumberFieldInput.tsx @@ -2,7 +2,7 @@ import { FormFieldHint } from '@/object-record/record-field/form-types/component 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 { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; import { TextInput } from '@/ui/field/input/components/TextInput'; import { InputLabel } from '@/ui/input/components/InputLabel'; @@ -117,7 +117,7 @@ export const FormNumberFieldInput = ({ disabled={readonly} /> ) : ( - diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSelectFieldInput.tsx index c302478a8..90c5aad7e 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSelectFieldInput.tsx @@ -1,7 +1,7 @@ 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 { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope'; import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList'; @@ -282,7 +282,7 @@ export const FormSelectFieldInput = ({ ) ) : ( - diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormUuidFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormUuidFieldInput.tsx index 5088c47c4..7cca096a5 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormUuidFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormUuidFieldInput.tsx @@ -1,7 +1,7 @@ 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 { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; import { TextInput } from '@/ui/field/input/components/TextInput'; import { InputLabel } from '@/ui/input/components/InputLabel'; @@ -108,7 +108,7 @@ export const FormUuidFieldInput = ({ onChange={handleChange} /> ) : ( - 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 cafe3b47f..7ba6392a3 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 @@ -1,32 +1,21 @@ import { extractVariableLabel } from '@/workflow/workflow-variables/utils/extractVariableLabel'; -import styled from '@emotion/styled'; - import { css, useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; import { IconX, isDefined } from 'twenty-ui'; -export const StyledContainer = styled.div` - align-items: center; - display: flex; -`; - const StyledChip = styled.div<{ deletable: boolean }>` - align-items: center; background-color: ${({ theme }) => theme.accent.quaternary}; border: 1px solid ${({ theme }) => theme.accent.tertiary}; border-radius: 4px; - color: ${({ theme }) => theme.color.blue}; - height: 26px; + height: 20px; box-sizing: border-box; cursor: pointer; - display: flex; + display: inline-flex; + align-items: center; flex-direction: row; flex-shrink: 0; column-gap: ${({ theme }) => theme.spacing(1)}; - font-size: ${({ theme }) => theme.font.size.sm}; - font-weight: ${({ theme }) => theme.font.weight.medium}; - padding: ${({ theme }) => theme.spacing(0.5)}; padding-left: ${({ theme }) => theme.spacing(1)}; - margin-left: ${({ theme }) => theme.spacing(2)}; user-select: none; white-space: nowrap; @@ -37,6 +26,11 @@ const StyledChip = styled.div<{ deletable: boolean }>` `} `; +const StyledLabel = styled.span` + color: ${({ theme }) => theme.color.blue}; + line-height: 140%; +`; + const StyledDelete = styled.button` box-sizing: border-box; height: 20px; @@ -51,11 +45,12 @@ const StyledDelete = styled.button` margin: 0; background: none; border: none; - color: inherit; + color: ${({ theme }) => theme.color.blue}; + border-top-right-radius: ${({ theme }) => theme.border.radius.sm}; + border-bottom-right-radius: ${({ theme }) => theme.border.radius.sm}; &:hover { background-color: ${({ theme }) => theme.accent.secondary}; - border-radius: ${({ theme }) => theme.border.radius.sm}; } `; @@ -71,16 +66,14 @@ export const VariableChip = ({ const theme = useTheme(); return ( - - - {extractVariableLabel(rawVariableName)} + + {extractVariableLabel(rawVariableName)} - {onRemove ? ( - - - - ) : null} - - + {onRemove ? ( + + + + ) : null} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChipStandalone.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChipStandalone.tsx new file mode 100644 index 000000000..f21aa0249 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/VariableChipStandalone.tsx @@ -0,0 +1,24 @@ +import { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import styled from '@emotion/styled'; + +const StyledContainer = styled.div` + align-items: center; + display: flex; + margin-left: ${({ theme }) => theme.spacing(2)}; +`; + +type VariableChipStandaloneProps = { + rawVariableName: string; + onRemove?: () => void; +}; + +export const VariableChipStandalone = ({ + rawVariableName, + onRemove, +}: VariableChipStandaloneProps) => { + return ( + + + + ); +}; 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 48fad9f9a..902a9fbce 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 @@ -110,14 +110,8 @@ export const DisplayDefaultValueWithVariablesProperly: Story = { await canvas.findByText(/{ "a": { "b" : /); - await waitFor(() => { - const variableTag = canvasElement.querySelector( - '[data-type="variableTag"]', - ); - - expect(variableTag).toBeVisible(); - expect(variableTag).toHaveTextContent('test'); - }); + const variableTag = await canvas.findByText('test'); + await expect(variableTag).toBeVisible(); await canvas.findByText(/ } }/); }, diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip.tsx index c1231d172..f5c8f9f5d 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip.tsx @@ -1,6 +1,6 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { RecordChip } from '@/object-record/components/RecordChip'; -import { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString'; import { @@ -48,7 +48,7 @@ export const WorkflowSingleRecordFieldChip = ({ isStandaloneVariableString(draftValue.value) ) { return ( - 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 new file mode 100644 index 000000000..885f9f5dd --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowTextEditorVariableChip.tsx @@ -0,0 +1,25 @@ +import { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip'; +import styled from '@emotion/styled'; +import { NodeViewProps, NodeViewWrapper } from '@tiptap/react'; + +const StyledWrapper = styled.span` + display: inline-block; + padding-inline: ${({ theme }) => theme.spacing(0.5)}; +`; + +type WorkflowTextEditorVariableChipProps = NodeViewProps; + +export const WorkflowTextEditorVariableChip = ({ + deleteNode, + node, +}: WorkflowTextEditorVariableChipProps) => { + const attrs = node.attrs as { + variable: string; + }; + + return ( + + + + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/variableTag.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/variableTag.ts index a7bda7a9e..a2b2675f3 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/variableTag.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/variableTag.ts @@ -1,6 +1,7 @@ +import { WorkflowTextEditorVariableChip } from '@/workflow/workflow-variables/components/WorkflowTextEditorVariableChip'; import { extractVariableLabel } from '@/workflow/workflow-variables/utils/extractVariableLabel'; import { Node } from '@tiptap/core'; -import { mergeAttributes } from '@tiptap/react'; +import { mergeAttributes, ReactNodeViewRenderer } from '@tiptap/react'; declare module '@tiptap/core' { interface Commands { @@ -41,6 +42,10 @@ export const VariableTag = Node.create({ ]; }, + addNodeView: () => { + return ReactNodeViewRenderer(WorkflowTextEditorVariableChip); + }, + renderText: ({ node }) => { return node.attrs.variable; },