fix: Input fields to have expected behaviour in case of empty / only whitespaces string (#6736)

# ISSUE
- Closes #6734
- Closes #6633
- Closes #6733
- Closes #6816

# Description

- [x] Don't allow Empty (whitespaces) Objects to Create, all the
keyboard shortcuts are also handled for this.



https://github.com/user-attachments/assets/1c9add4e-f13f-458b-8f76-63bd868413a2



https://github.com/user-attachments/assets/e72b6ee3-74e4-4517-a230-3eb10db80dc7

Note: we do have one other issue with FullName field #6740 Inorder to
test use **shift**.




- [x] Api Keys Input Name Field -> New and Detail View Name field
shouldn't be empty or string with whitespaces, we won't able to have
whitespaces in both. **Try Entering just spaces**



https://github.com/user-attachments/assets/b521b49f-648c-4585-9d15-8ff4faed3c3a


- [x] Similar to above, Empty webhook endpoint url under
**/settings/developers/webhooks/new** won't be created. **Try Entering
just spaces**

- [x] New Functions or Updating Functions will not able to have
whitespaces, empty string as Name. **Try Entering just spaces**



https://github.com/user-attachments/assets/09fcf394-c6d9-4080-8efd-462b054a22d0



- [x] under **settings/workspace-members** changes will lead and solve
that user won't be able to enter Invite by email as just whitespaces +
button is now getting disabled when there is no correct email. **Try
Entering just spaces**



https://github.com/user-attachments/assets/b352edfa-113b-4645-80fd-db6f120ab5db



- [x] Text Input Field, will not allow to start entering with
whitespaces and won't take just whitespaces as value spaces between
words will work.


https://github.com/user-attachments/assets/8c1a0812-45be-4ed2-bd3d-bb4f92147976

- [x] Similarly Number works as per above including shortcuts. 


https://github.com/user-attachments/assets/9f69cc87-5c3c-43ee-93c4-fa887bc0d7ee


- [x] Similarly FullName field works as per above including shortcuts



https://github.com/user-attachments/assets/7bb006b2-abf7-44cd-a214-7a2fc68df169

- [x] Pasting fullName is been Improved. 

- Case 1 (Two Words): If there are exactly two words, return them as is.
- Case 2 (More than Two Words): If there are more than two words, return
the first two words only.
- Case 3 (One Word): If there is only one word, return it as the first
name, with an empty string as the last name.
- WhiteSpaces have been handled. 

```
console.log(splitFullName("John     Doe")); // ["John", "Doe"]
console.log(splitFullName("        ")); // ["", ""]
console.log(splitFullName("John")); // ["John", ""]
console.log(splitFullName("  John   Doe  ")); // ["John", "Doe"]
console.log(splitFullName("John Michael  Andrew Doe")); // ["John", "Michael"]
```

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Nabhag Motivaras
2024-09-21 00:55:50 +05:30
committed by GitHub
parent c8c1890ad7
commit c97c71762e
22 changed files with 249 additions and 187 deletions

View File

@ -3,8 +3,7 @@ import { FieldDoubleText } from '@/object-record/record-field/types/FieldDoubleT
import { DoubleTextInput } from '@/ui/field/input/components/DoubleTextInput';
import { FieldInputOverlay } from '@/ui/field/input/components/FieldInputOverlay';
import { usePersistField } from '../../../hooks/usePersistField';
import { isDoubleTextFieldEmpty } from '@/object-record/record-field/meta-types/input/utils/isDoubleTextFieldEmpty';
import { FieldInputEvent } from './DateTimeFieldInput';
const FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS =
@ -28,46 +27,55 @@ export const FullNameFieldInput = ({
onTab,
onShiftTab,
}: FullNameFieldInputProps) => {
const { hotkeyScope, draftValue, setDraftValue } = useFullNameField();
const persistField = usePersistField();
const { hotkeyScope, draftValue, setDraftValue, persistFullNameField } =
useFullNameField();
const convertToFullName = (newDoubleText: FieldDoubleText) => {
return {
firstName: newDoubleText.firstValue,
lastName: newDoubleText.secondValue,
firstName: newDoubleText.firstValue.trim(),
lastName: newDoubleText.secondValue.trim(),
};
};
const getRequiredDraftValueFromDoubleText = (
newDoubleText: FieldDoubleText,
) => {
return isDoubleTextFieldEmpty(newDoubleText)
? undefined
: convertToFullName(newDoubleText);
};
const handleEnter = (newDoubleText: FieldDoubleText) => {
onEnter?.(() => persistField(convertToFullName(newDoubleText)));
onEnter?.(() => persistFullNameField(convertToFullName(newDoubleText)));
};
const handleEscape = (newDoubleText: FieldDoubleText) => {
onEscape?.(() => persistField(convertToFullName(newDoubleText)));
onEscape?.(() => persistFullNameField(convertToFullName(newDoubleText)));
};
const handleClickOutside = (
event: MouseEvent | TouchEvent,
newDoubleText: FieldDoubleText,
) => {
onClickOutside?.(() => persistField(convertToFullName(newDoubleText)));
onClickOutside?.(() =>
persistFullNameField(convertToFullName(newDoubleText)),
);
};
const handleTab = (newDoubleText: FieldDoubleText) => {
onTab?.(() => persistField(convertToFullName(newDoubleText)));
onTab?.(() => persistFullNameField(convertToFullName(newDoubleText)));
};
const handleShiftTab = (newDoubleText: FieldDoubleText) => {
onShiftTab?.(() => persistField(convertToFullName(newDoubleText)));
onShiftTab?.(() => persistFullNameField(convertToFullName(newDoubleText)));
};
const handleChange = (newDoubleText: FieldDoubleText) => {
setDraftValue(convertToFullName(newDoubleText));
setDraftValue(getRequiredDraftValueFromDoubleText(newDoubleText));
};
const handlePaste = (newDoubleText: FieldDoubleText) => {
setDraftValue(convertToFullName(newDoubleText));
setDraftValue(getRequiredDraftValueFromDoubleText(newDoubleText));
};
return (

View File

@ -16,6 +16,7 @@ import { MenuItemMultiSelectTag } from '@/ui/navigation/menu-item/components/Men
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from '~/utils/isDefined';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
const StyledRelationPickerContainer = styled.div`
left: -1px;
@ -109,7 +110,11 @@ export const MultiSelectFieldInput = ({
<DropdownMenu data-select-disable>
<DropdownMenuSearchInput
value={searchFilter}
onChange={(event) => setSearchFilter(event.currentTarget.value)}
onChange={(event) =>
setSearchFilter(
turnIntoEmptyStringIfWhitespacesOnly(event.currentTarget.value),
)
}
autoFocus
/>
<DropdownMenuSeparator />

View File

@ -60,7 +60,7 @@ export const NumberFieldInput = ({
<TextInput
placeholder={fieldDefinition.metadata.placeHolder}
autoFocus
value={draftValue ?? ''}
value={draftValue?.toString() ?? ''}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}

View File

@ -4,6 +4,7 @@ import { TextAreaInput } from '@/ui/field/input/components/TextAreaInput';
import { usePersistField } from '../../../hooks/usePersistField';
import { useTextField } from '../../hooks/useTextField';
import { turnIntoUndefinedIfWhitespacesOnly } from '~/utils/string/turnIntoUndefinedIfWhitespacesOnly';
import { FieldInputEvent } from './DateTimeFieldInput';
export type TextFieldInputProps = {
@ -25,32 +26,31 @@ export const TextFieldInput = ({
useTextField();
const persistField = usePersistField();
const handleEnter = (newText: string) => {
onEnter?.(() => persistField(newText));
onEnter?.(() => persistField(newText.trim()));
};
const handleEscape = (newText: string) => {
onEscape?.(() => persistField(newText));
onEscape?.(() => persistField(newText.trim()));
};
const handleClickOutside = (
event: MouseEvent | TouchEvent,
newText: string,
) => {
onClickOutside?.(() => persistField(newText));
onClickOutside?.(() => persistField(newText.trim()));
};
const handleTab = (newText: string) => {
onTab?.(() => persistField(newText));
onTab?.(() => persistField(newText.trim()));
};
const handleShiftTab = (newText: string) => {
onShiftTab?.(() => persistField(newText));
onShiftTab?.(() => persistField(newText.trim()));
};
const handleChange = (newText: string) => {
setDraftValue(newText);
setDraftValue(turnIntoUndefinedIfWhitespacesOnly(newText));
};
return (

View File

@ -0,0 +1,9 @@
import { FieldDoubleText } from '@/object-record/record-field/types/FieldDoubleText';
export const isDoubleTextFieldEmpty = (doubleText: FieldDoubleText) => {
const { firstValue, secondValue } = doubleText;
const totalLength = firstValue.trim().length + secondValue.trim().length;
return totalLength > 0 ? false : true;
};

View File

@ -25,7 +25,7 @@ import {
} from '@/object-record/record-field/types/FieldMetadata';
export type FieldTextDraftValue = string;
export type FieldNumberDraftValue = string;
export type FieldNumberDraftValue = number;
export type FieldDateTimeDraftValue = string;
export type FieldPhoneDraftValue = string;
export type FieldPhonesDraftValue = {

View File

@ -1,5 +1,5 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useRef } from 'react';
import { Fragment, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { IconComponent, IconPlus } from 'twenty-ui';
@ -158,16 +158,15 @@ export const SingleEntitySelectMenuItems = ({
switch (entity.id) {
case 'add-new': {
return (
<>
<Fragment key={entity.id}>
{entitiesToSelect.length > 0 && <DropdownMenuSeparator />}
<CreateNewButton
key={entity.id}
onClick={onCreate}
LeftIcon={IconPlus}
text="Add New"
hovered={isSelectedAddNewButton}
/>
</>
</Fragment>
);
}
case 'select-none': {