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

@ -13,6 +13,8 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from '~/utils/isDefined';
import { splitFullName } from '~/utils/format/spiltFullName';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
import { StyledTextInput } from './TextInput';
const StyledContainer = styled.div`
@ -167,9 +169,12 @@ export const DoubleTextInput = ({
const name = event.clipboardData.getData('Text');
const splittedName = name.split(' ');
const splittedName = splitFullName(name);
onPaste?.({ firstValue: splittedName[0], secondValue: splittedName[1] });
onPaste?.({
firstValue: splittedName[0],
secondValue: splittedName[1],
});
};
const handleClickToPreventParentClickEvents = (
@ -189,7 +194,10 @@ export const DoubleTextInput = ({
placeholder={firstValuePlaceholder}
value={firstInternalValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
handleChange(event.target.value, secondInternalValue);
handleChange(
turnIntoEmptyStringIfWhitespacesOnly(event.target.value),
secondInternalValue,
);
}}
onPaste={(event: ClipboardEvent<HTMLInputElement>) =>
handleOnPaste(event)
@ -203,7 +211,10 @@ export const DoubleTextInput = ({
placeholder={secondValuePlaceholder}
value={secondInternalValue}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
handleChange(firstInternalValue, event.target.value);
handleChange(
firstInternalValue,
turnIntoEmptyStringIfWhitespacesOnly(event.target.value),
);
}}
onClick={handleClickToPreventParentClickEvents}
/>

View File

@ -1,11 +1,12 @@
import styled from '@emotion/styled';
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import styled from '@emotion/styled';
import { TEXT_INPUT_STYLE } from 'twenty-ui';
import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
import { isDefined } from '~/utils/isDefined';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
export type TextAreaInputProps = {
disabled?: boolean;
@ -67,10 +68,12 @@ export const TextAreaInput = ({
copyButton = true,
}: TextAreaInputProps) => {
const [internalText, setInternalText] = useState(value);
const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
setInternalText(event.target.value);
onChange?.(event.target.value);
const targetValue = turnIntoEmptyStringIfWhitespacesOnly(
event.target.value,
);
setInternalText(targetValue);
onChange?.(targetValue);
};
const wrapperRef = useRef<HTMLTextAreaElement>(null);

View File

@ -1,5 +1,5 @@
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import styled from '@emotion/styled';
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import { TEXT_INPUT_STYLE } from 'twenty-ui';
import { LightCopyIconButton } from '@/object-record/record-field/components/LightCopyIconButton';
@ -44,12 +44,11 @@ export const TextInput = ({
const copyRef = useRef<HTMLDivElement>(null);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setInternalText(event.target.value);
onChange?.(event.target.value);
setInternalText(event.target.value.trim());
onChange?.(event.target.value.trim());
};
useEffect(() => {
setInternalText(value);
setInternalText(value.trim());
}, [value]);
useRegisterInputEvents({

View File

@ -1,9 +1,10 @@
import styled from '@emotion/styled';
import { FocusEventHandler } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import styled from '@emotion/styled';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
import { InputHotkeyScope } from '../types/InputHotkeyScope';
const MAX_ROWS = 5;
@ -75,7 +76,9 @@ export const TextArea = ({
maxRows={MAX_ROWS}
minRows={computedMinRows}
value={value}
onChange={(event) => onChange?.(event.target.value)}
onChange={(event) =>
onChange?.(turnIntoEmptyStringIfWhitespacesOnly(event.target.value))
}
onFocus={handleFocus}
onBlur={handleBlur}
disabled={disabled}

View File

@ -11,6 +11,7 @@ import {
} from 'react';
import { IconComponent, IconEye, IconEyeOff } from 'twenty-ui';
import { useCombinedRefs } from '~/hooks/useCombinedRefs';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
const StyledContainer = styled.div<
Pick<TextInputV2ComponentProps, 'fullWidth'>
@ -180,7 +181,7 @@ const TextInputV2Component = (
</StyledLeftIconContainer>
)}
<StyledInput
data-testId={dataTestId}
data-testid={dataTestId}
autoComplete={autoComplete || 'off'}
ref={combinedRef}
tabIndex={tabIndex ?? 0}
@ -188,7 +189,9 @@ const TextInputV2Component = (
onBlur={onBlur}
type={passwordVisible ? 'text' : type}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
onChange?.(event.target.value);
onChange?.(
turnIntoEmptyStringIfWhitespacesOnly(event.target.value),
);
}}
onKeyDown={onKeyDown}
{...{

View File

@ -30,7 +30,6 @@ const StyledTopBarContainer = styled.div<{ width?: number }>`
padding: ${({ theme }) => theme.spacing(2)};
padding-left: 0;
padding-right: ${({ theme }) => theme.spacing(3)};
z-index: 20;
width: ${({ width }) => width + 'px' || '100%'};
@media (max-width: ${MOBILE_VIEWPORT}px) {