diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordPicker.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordPicker.tsx index f53d7ef49..d8ac76070 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordPicker.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordPicker.tsx @@ -15,7 +15,7 @@ import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-sta import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString'; import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { useCallback } from 'react'; +import { useCallback, useId } from 'react'; import { isDefined, isValidUuid } from 'twenty-shared/utils'; import { IconChevronDown, IconForbid } from 'twenty-ui/display'; @@ -98,8 +98,9 @@ export const FormSingleRecordPicker = ({ skip: !isDefined(defaultValue) || !isValidUuid(defaultValue), }); - const dropdownId = `form-record-picker-${objectNameSingular}`; - const variablesDropdownId = `form-record-picker-${objectNameSingular}-variables`; + const componentId = useId(); + const dropdownId = `form-record-picker-${componentId}`; + const variablesDropdownId = `form-record-picker-${componentId}-variables`; const { closeDropdown } = useDropdown(dropdownId); diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormSingleRecordPicker.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormSingleRecordPicker.stories.tsx index a05e13438..ae1900c38 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormSingleRecordPicker.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/__stories__/FormSingleRecordPicker.stories.tsx @@ -41,9 +41,13 @@ export const Default: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await canvas.findByText('Company'); + const label = await canvas.findByText('Company'); + expect(label).toBeVisible(); + const dropdown = await canvas.findByRole('button'); expect(dropdown).toBeVisible(); + + await userEvent.click(dropdown); }, }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx index b2200d316..af42e3dec 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx @@ -1,10 +1,15 @@ import { WorkflowFormAction } from '@/workflow/types/Workflow'; import { Meta, StoryObj } from '@storybook/react'; -import { expect, within } from '@storybook/test'; +import { expect, userEvent, waitFor, within } from '@storybook/test'; import { FieldMetadataType } from 'twenty-shared/types'; -import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing'; +import { + ComponentDecorator, + getCanvasElementForDropdownTesting, + RouterDecorator, +} from 'twenty-ui/testing'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; +import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator'; import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorator'; import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator'; @@ -25,6 +30,7 @@ const meta: Meta = { RouterDecorator, ObjectMetadataItemsDecorator, WorkspaceDecorator, + SnackBarDecorator, ], }; @@ -81,6 +87,42 @@ const mockAction: WorkflowFormAction = { }, }; +const mockActionWithDuplicatedRecordFields: WorkflowFormAction = { + id: 'form-action-1', + type: 'FORM', + name: 'Test Form', + valid: true, + settings: { + input: [ + { + id: 'field-1', + name: 'record', + label: 'Record', + type: 'RECORD', + placeholder: 'Select a record', + settings: { + objectName: 'company', + }, + }, + { + id: 'field-2', + name: 'record', + label: 'Record', + type: 'RECORD', + placeholder: 'Select a record', + settings: { + objectName: 'company', + }, + }, + ], + outputSchema: {}, + errorHandlingOptions: { + retryOnFailure: { value: false }, + continueOnFailure: { value: false }, + }, + }, +}; + export const Default: Story = { args: { action: mockAction, @@ -124,7 +166,41 @@ export const ReadonlyMode: Story = { const dateInput = await canvas.findByPlaceholderText('mm/dd/yyyy'); expect(dateInput).toBeDisabled(); - const submitButton = await canvas.queryByText('Submit'); + const submitButton = canvas.queryByText('Submit'); expect(submitButton).not.toBeInTheDocument(); }, }; + +export const CanHaveManyRecordFieldsForTheSameRecordType: Story = { + args: { + action: mockActionWithDuplicatedRecordFields, + actionOptions: { + readonly: false, + }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const recordSelects = await waitFor(() => { + const elements = canvas.getAllByText('Select a company'); + + expect(elements.length).toBe(2); + + return elements; + }); + + for (const recordSelect of recordSelects) { + expect(recordSelect).toBeVisible(); + + await userEvent.click(recordSelect); + + await waitFor(() => { + expect( + within(getCanvasElementForDropdownTesting()).getByText('Louis Duss'), + ).toBeVisible(); + }); + + await userEvent.click(canvasElement); + } + }, +};