feat: trim empty space (#12293)
Trim trailing spaces of the input, instead of manually trimming, thus improving the user experience. Fixes #12279 ### Screencast [Screencast from 2025-05-26 21-03-54.webm](https://github.com/user-attachments/assets/cc40be5a-d260-4a20-bbc8-c0b21ddbbd9b) --------- Co-authored-by: Devessier <baptiste@devessier.fr>
This commit is contained in:
@ -132,8 +132,9 @@ export const MultiItemFieldInput = <T,>({
|
||||
};
|
||||
|
||||
const handleSubmitInput = () => {
|
||||
const sanitizedInput = inputValue.trim();
|
||||
if (validateInput !== undefined) {
|
||||
const validationData = validateInput(inputValue) ?? { isValid: true };
|
||||
const validationData = validateInput(sanitizedInput) ?? { isValid: true };
|
||||
if (!validationData.isValid) {
|
||||
onError?.(true, items);
|
||||
setErrorData(validationData);
|
||||
@ -141,18 +142,18 @@ export const MultiItemFieldInput = <T,>({
|
||||
}
|
||||
}
|
||||
|
||||
if (inputValue === '' && isAddingNewItem) {
|
||||
if (sanitizedInput === '' && isAddingNewItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputValue === '' && !isAddingNewItem) {
|
||||
if (sanitizedInput === '' && !isAddingNewItem) {
|
||||
handleDeleteItem(itemToEditIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
const newItem = formatInput
|
||||
? formatInput(inputValue)
|
||||
: (inputValue as unknown as T);
|
||||
? formatInput(sanitizedInput)
|
||||
: (sanitizedInput as unknown as T);
|
||||
|
||||
if (!isAddingNewItem && newItem === items[itemToEditIndex]) {
|
||||
setIsInputDisplayed(false);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { fn, userEvent, within } from '@storybook/test';
|
||||
import { fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
@ -125,3 +125,39 @@ export const Default: Story = {
|
||||
expect(tag3Element).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const TrimInput: Story = {
|
||||
args: {
|
||||
value: ['tag1', 'tag2'],
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const addButton = await canvas.findByText('Add Item');
|
||||
await userEvent.click(addButton);
|
||||
|
||||
const input = await canvas.findByPlaceholderText('Enter value');
|
||||
await userEvent.type(input, ' tag2 {enter}');
|
||||
|
||||
await waitFor(() => {
|
||||
const tag2Elements = canvas.queryAllByText('tag2');
|
||||
expect(tag2Elements).toHaveLength(2);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateRecord).toHaveBeenCalledWith({
|
||||
variables: {
|
||||
where: { id: 'record-id' },
|
||||
updateOneRecordInput: {
|
||||
tags: [
|
||||
'tag1',
|
||||
'tag2',
|
||||
'tag2', // The second tag2 is not trimmed, so it remains as a duplicate
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(updateRecord).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { fn, userEvent, within } from '@storybook/test';
|
||||
import { fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect } from 'react';
|
||||
import { getCanvasElementForDropdownTesting } from 'twenty-ui/testing';
|
||||
|
||||
@ -131,7 +131,42 @@ export const Default: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
// FIXME: We will have to fix that behavior, we should only be able to set a secondary email as the primary email
|
||||
export const TrimInput: Story = {
|
||||
args: {
|
||||
value: {
|
||||
primaryEmail: 'john@example.com',
|
||||
additionalEmails: [],
|
||||
},
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const addButton = await canvas.findByText('Add Email');
|
||||
await userEvent.click(addButton);
|
||||
|
||||
const input = await canvas.findByPlaceholderText('Email');
|
||||
await userEvent.type(input, ' new.email@example.com {enter}');
|
||||
|
||||
const newEmailElement = await canvas.findByText('new.email@example.com');
|
||||
expect(newEmailElement).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateRecord).toHaveBeenCalledWith({
|
||||
variables: {
|
||||
where: { id: 'record-id' },
|
||||
updateOneRecordInput: {
|
||||
emails: {
|
||||
primaryEmail: 'john@example.com',
|
||||
additionalEmails: ['new.email@example.com'],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(updateRecord).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
|
||||
export const CanNotSetPrimaryLinkAsPrimaryLink: Story = {
|
||||
args: {
|
||||
value: {
|
||||
|
||||
@ -253,6 +253,36 @@ export const CreatePrimaryLink: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const TrimInput: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const input = await canvas.findByPlaceholderText('URL');
|
||||
await userEvent.type(input, ' https://www.twenty.com {enter}');
|
||||
|
||||
const linkDisplay = await canvas.findByText('twenty.com');
|
||||
expect(linkDisplay).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(updateRecord).toHaveBeenCalledWith({
|
||||
variables: {
|
||||
where: { id: '123' },
|
||||
updateOneRecordInput: {
|
||||
links: {
|
||||
primaryLinkUrl: 'https://www.twenty.com',
|
||||
primaryLinkLabel: null,
|
||||
secondaryLinks: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(updateRecord).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(getPrimaryLinkBookmarkIcon(canvasElement)).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const AddSecondaryLink: Story = {
|
||||
args: {
|
||||
value: {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { fn, userEvent, within } from '@storybook/test';
|
||||
import { fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect } from 'react';
|
||||
import { getCanvasElementForDropdownTesting } from 'twenty-ui/testing';
|
||||
|
||||
@ -140,7 +140,64 @@ export const Default: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
// FIXME: We will have to fix that behavior, we should only be able to set an additional phone as the primary phone
|
||||
export const TrimInput: Story = {
|
||||
args: {
|
||||
value: {
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneNumber: '642646272',
|
||||
primaryPhoneCallingCode: '+33',
|
||||
additionalPhones: [
|
||||
{
|
||||
countryCode: 'FR',
|
||||
number: '642646273',
|
||||
callingCode: '+33',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const addButton = await canvas.findByText('Add Phone');
|
||||
await userEvent.click(addButton);
|
||||
|
||||
const input = await canvas.findByPlaceholderText('Phone');
|
||||
await userEvent.type(input, '+33642646274 {enter}');
|
||||
|
||||
const newPhoneElement = await canvas.findByText('+33 6 42 64 62 74');
|
||||
expect(newPhoneElement).toBeVisible();
|
||||
|
||||
// Verify the update was called with swapped phones
|
||||
await waitFor(() => {
|
||||
expect(updateRecord).toHaveBeenCalledWith({
|
||||
variables: {
|
||||
where: { id: 'record-id' },
|
||||
updateOneRecordInput: {
|
||||
phones: {
|
||||
primaryPhoneCallingCode: '+33',
|
||||
primaryPhoneCountryCode: 'FR',
|
||||
primaryPhoneNumber: '642646272',
|
||||
additionalPhones: [
|
||||
{
|
||||
countryCode: 'FR',
|
||||
number: '642646273',
|
||||
callingCode: '+33',
|
||||
},
|
||||
{
|
||||
countryCode: 'FR',
|
||||
number: '642646274',
|
||||
callingCode: '+33',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(updateRecord).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
|
||||
export const CanNotSetPrimaryLinkAsPrimaryLink: Story = {
|
||||
args: {
|
||||
value: {
|
||||
|
||||
Reference in New Issue
Block a user