diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormCountryCodeSelectInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormCallingCodeSelectInput.tsx similarity index 54% rename from packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormCountryCodeSelectInput.tsx rename to packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormCallingCodeSelectInput.tsx index 902f04456e..2c4b17ac23 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormCountryCodeSelectInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormCallingCodeSelectInput.tsx @@ -3,22 +3,27 @@ import { useMemo } from 'react'; import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput'; import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent'; import { useCountries } from '@/ui/input/components/internal/hooks/useCountries'; +import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString'; import { t } from '@lingui/core/macro'; -import type { CountryCode } from 'libphonenumber-js'; import { IconCircleOff, type IconComponentProps } from 'twenty-ui/display'; import { type SelectOption } from 'twenty-ui/input'; -export type FormCountryCodeSelectInputUpdatedValue = CountryCode | ''; +export type FormCallingCodeSelectInputUpdatedValue = { + callingCode: string; + countryCode: string; +}; -export const FormCountryCodeSelectInput = ({ +export const FormCallingCodeSelectInput = ({ selectedCountryCode, + selectedCallingCode, onChange, label, readonly = false, VariablePicker, }: { selectedCountryCode: string; - onChange: (countryCode: FormCountryCodeSelectInputUpdatedValue) => void; + selectedCallingCode?: string; + onChange: (value: FormCallingCodeSelectInputUpdatedValue) => void; label?: string; readonly?: boolean; VariablePicker?: VariablePickerComponent; @@ -28,7 +33,7 @@ export const FormCountryCodeSelectInput = ({ const options: SelectOption[] = useMemo(() => { const countryList = countries.map( ({ countryName, countryCode, callingCode, Flag }) => ({ - label: `${countryName} (+${callingCode})`, + label: `+${callingCode} (${countryName})`, value: countryCode, Icon: (props: IconComponentProps) => Flag({ width: props.size, height: props.size }), @@ -36,7 +41,7 @@ export const FormCountryCodeSelectInput = ({ ); return [ { - label: t`No country`, + label: t`No calling code`, value: '', Icon: IconCircleOff, }, @@ -44,24 +49,43 @@ export const FormCountryCodeSelectInput = ({ ]; }, [countries]); - const onCountryCodeChange = (countryCode: string | null) => { + const defaultValue = isStandaloneVariableString(selectedCallingCode) + ? selectedCallingCode + : selectedCountryCode; + + const handleChange = (value: string | null) => { if (readonly) { return; } - if (countryCode === null) { - onChange(''); - } else { - onChange(countryCode as CountryCode); + if (value === null) { + onChange({ callingCode: '', countryCode: '' }); + return; } + + if (isStandaloneVariableString(value)) { + onChange({ callingCode: value, countryCode: '' }); + return; + } + + const matchingCountry = countries.find( + (countryItem) => countryItem.countryCode === value, + ); + + const callingCode = matchingCountry?.callingCode; + + onChange({ + callingCode: callingCode ? `+${callingCode}` : '', + countryCode: value, + }); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormPhoneFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormPhoneFieldInput.tsx index 0e8fb96e22..7139216639 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormPhoneFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormPhoneFieldInput.tsx @@ -1,15 +1,14 @@ -import { t } from '@lingui/core/macro'; import { - FormCountryCodeSelectInput, - type FormCountryCodeSelectInputUpdatedValue, -} from '@/object-record/record-field/ui/form-types/components/FormCountryCodeSelectInput'; + FormCallingCodeSelectInput, + type FormCallingCodeSelectInputUpdatedValue, +} from '@/object-record/record-field/ui/form-types/components/FormCallingCodeSelectInput'; import { FormFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormFieldInputContainer'; import { FormNestedFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormNestedFieldInputContainer'; import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types/components/FormNumberFieldInput'; import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent'; import { type FieldPhonesValue } from '@/object-record/record-field/ui/types/FieldMetadata'; import { InputLabel } from '@/ui/input/components/InputLabel'; -import { getCountryCallingCode } from 'libphonenumber-js'; +import { t } from '@lingui/core/macro'; type FormPhoneFieldInputProps = { label?: string; @@ -26,19 +25,12 @@ export const FormPhoneFieldInput = ({ readonly, VariablePicker, }: FormPhoneFieldInputProps) => { - const handleCountryChange = ( - newCountry: FormCountryCodeSelectInputUpdatedValue, + const handleCallingCodeChange = ( + newValue: FormCallingCodeSelectInputUpdatedValue, ) => { - let newCallingCode; - if (newCountry === '') { - newCallingCode = ''; - } else { - newCallingCode = getCountryCallingCode(newCountry); - } - onChange({ - primaryPhoneCountryCode: newCountry, - primaryPhoneCallingCode: newCallingCode, + primaryPhoneCountryCode: newValue.countryCode, + primaryPhoneCallingCode: newValue.callingCode, primaryPhoneNumber: defaultValue?.primaryPhoneNumber ?? '', }); }; @@ -55,11 +47,13 @@ export const FormPhoneFieldInput = ({ {label && {label}} - = { + title: 'UI/Data/Field/Form/Input/FormCallingCodeSelectInput', + component: FormCallingCodeSelectInput, + args: { + label: 'Calling Code', + }, + argTypes: {}, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + selectedCountryCode: 'FR', + selectedCallingCode: '+33', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await canvas.findByText('Calling Code'); + }, +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/__stories__/FormCountryCodeSelectInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/__stories__/FormCountryCodeSelectInput.stories.tsx deleted file mode 100644 index 1ffb54744b..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/__stories__/FormCountryCodeSelectInput.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { type Meta, type StoryObj } from '@storybook/react-vite'; -import { within } from 'storybook/test'; - -import { FormCountryCodeSelectInput } from '@/object-record/record-field/ui/form-types/components/FormCountryCodeSelectInput'; - -const meta: Meta = { - title: 'UI/Data/Field/Form/Input/FormCountryCodeSelectInput', - component: FormCountryCodeSelectInput, - args: { - label: 'Country Code', - }, - argTypes: {}, -}; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: { - selectedCountryCode: 'FR', - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await canvas.findByText('Country Code'); - }, -}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/__stories__/FormPhoneFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/__stories__/FormPhoneFieldInput.stories.tsx index 56922c5a85..5cfb1ef3ac 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/__stories__/FormPhoneFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/__stories__/FormPhoneFieldInput.stories.tsx @@ -22,7 +22,7 @@ type Story = StoryObj; const defaultPhoneValue: FieldPhonesValue = { primaryPhoneNumber: '0612345678', primaryPhoneCountryCode: 'FR', - primaryPhoneCallingCode: '33', + primaryPhoneCallingCode: '+33', }; const FormPhoneFieldInputWithState = ({ @@ -66,7 +66,8 @@ export const WithVariablesAsDefaultValues: Story = { args: { label: 'Phone', defaultValue: { - primaryPhoneCountryCode: `{{${MOCKED_STEP_ID}.name}}`, + primaryPhoneCountryCode: '', + primaryPhoneCallingCode: `{{${MOCKED_STEP_ID}.name}}`, primaryPhoneNumber: `{{${MOCKED_STEP_ID}.amount.amountMicros}}`, }, VariablePicker: () =>
VariablePicker
, @@ -74,12 +75,12 @@ export const WithVariablesAsDefaultValues: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const countryCodeVariable = await canvas.findByText('Name'); - expect(countryCodeVariable).toBeVisible(); + const callingCodeVariable = await canvas.findByText('Name'); + expect(callingCodeVariable).toBeVisible(); const variablePickers = await canvas.findAllByText('VariablePicker'); - expect(variablePickers).toHaveLength(1); + expect(variablePickers).toHaveLength(2); for (const variablePicker of variablePickers) { expect(variablePicker).toBeVisible(); @@ -115,14 +116,16 @@ export const SelectingVariables: Story = { play: async ({ canvasElement, args }) => { const canvas = within(canvasElement); - const countryCodeDefaultValue = await canvas.findByText('No country'); - expect(countryCodeDefaultValue).toBeVisible(); + const callingCodeDefaultValue = await canvas.findByText('No calling code'); + + expect(callingCodeDefaultValue).toBeVisible(); const phoneNumberDefaultValue = await canvas.findByPlaceholderText('Enter phone number'); expect(phoneNumberDefaultValue).toHaveDisplayValue(''); - const phoneNumberVariablePicker = await canvas.findByText('Add variable'); + const addVariableButtons = await canvas.findAllByText('Add variable'); + const phoneNumberVariablePicker = addVariableButtons[1]; await userEvent.click(phoneNumberVariablePicker); @@ -148,10 +151,10 @@ export const Disabled: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const countryInput = await canvas.findByText('No country'); - expect(countryInput).toBeVisible(); + const callingCodeInput = await canvas.findByText('No calling code'); + expect(callingCodeInput).toBeVisible(); - await userEvent.click(countryInput); + await userEvent.click(callingCodeInput); const searchInputInModal = canvas.queryByPlaceholderText('Search'); expect(searchInputInModal).not.toBeInTheDocument();