Add full name composite field (#9008)

- Add composite field
- Add test
- Fix search variable dropdown scroll
<img width="548" alt="Capture d’écran 2024-12-10 à 16 52 43"
src="https://github.com/user-attachments/assets/c337f0c3-8a70-401a-abd0-7206489ba73e">
This commit is contained in:
Thomas Trompette
2024-12-10 17:29:55 +01:00
committed by GitHub
parent 96d56f8883
commit b6e02b630d
13 changed files with 148 additions and 33 deletions

View File

@ -1,11 +1,16 @@
import { FormBooleanFieldInput } from '@/object-record/record-field/form-types/components/FormBooleanFieldInput';
import { FormFullNameFieldInput } from '@/object-record/record-field/form-types/components/FormFullNameFieldInput';
import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
import { FormSelectFieldInput } from '@/object-record/record-field/form-types/components/FormSelectFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import {
FieldFullNameValue,
FieldMetadata,
} from '@/object-record/record-field/types/FieldMetadata';
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
@ -55,5 +60,12 @@ export const FormFieldInput = ({
field={field}
VariablePicker={VariablePicker}
/>
) : isFieldFullName(field) ? (
<FormFullNameFieldInput
label={field.label}
defaultValue={defaultValue as FieldFullNameValue | undefined}
onPersist={onPersist}
VariablePicker={VariablePicker}
/>
) : null;
};

View File

@ -0,0 +1,66 @@
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
import { StyledFormCompositeFieldInputContainer } from '@/object-record/record-field/form-types/components/StyledFormCompositeFieldInputContainer';
import { StyledFormFieldInputContainer } from '@/object-record/record-field/form-types/components/StyledFormFieldInputContainer';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS } from '@/object-record/record-field/meta-types/input/constants/FirstNamePlaceholder';
import { LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS } from '@/object-record/record-field/meta-types/input/constants/LastNamePlaceholder';
import { FieldFullNameValue } from '@/object-record/record-field/types/FieldMetadata';
import { InputLabel } from '@/ui/input/components/InputLabel';
type FormFullNameFieldInputProps = {
label?: string;
defaultValue: FieldFullNameValue | undefined;
onPersist: (value: FieldFullNameValue) => void;
VariablePicker?: VariablePickerComponent;
readonly?: boolean;
};
export const FormFullNameFieldInput = ({
label,
defaultValue,
onPersist,
readonly,
VariablePicker,
}: FormFullNameFieldInputProps) => {
const handleFirstNameChange = (newText: string) => {
onPersist({
lastName: defaultValue?.lastName ?? '',
firstName: newText,
});
};
const handleLastNameChange = (newText: string) => {
onPersist({
firstName: defaultValue?.firstName ?? '',
lastName: newText,
});
};
return (
<StyledFormFieldInputContainer>
{label ? <InputLabel>{label}</InputLabel> : null}
<StyledFormCompositeFieldInputContainer>
<FormTextFieldInput
label="First Name"
defaultValue={defaultValue?.firstName}
onPersist={handleFirstNameChange}
placeholder={
FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS
}
readonly={readonly}
VariablePicker={VariablePicker}
/>
<FormTextFieldInput
label="Last Name"
defaultValue={defaultValue?.lastName}
onPersist={handleLastNameChange}
placeholder={
LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS
}
readonly={readonly}
VariablePicker={VariablePicker}
/>
</StyledFormCompositeFieldInputContainer>
</StyledFormFieldInputContainer>
);
};

View File

@ -0,0 +1,11 @@
import styled from '@emotion/styled';
export const StyledFormCompositeFieldInputContainer = styled.div`
display: flex;
flex-direction: column;
background: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
`;

View File

@ -3,4 +3,5 @@ import styled from '@emotion/styled';
export const StyledFormFieldInputContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
`;

View File

@ -0,0 +1,31 @@
import { Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/test';
import { FormFullNameFieldInput } from '../FormFullNameFieldInput';
const meta: Meta<typeof FormFullNameFieldInput> = {
title: 'UI/Data/Field/Form/Input/FormFullNameFieldInput',
component: FormFullNameFieldInput,
args: {},
argTypes: {},
};
export default meta;
type Story = StoryObj<typeof FormFullNameFieldInput>;
export const Default: Story = {
args: {
label: 'Name',
defaultValue: {
firstName: 'John',
lastName: 'Doe',
},
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText('Name');
await canvas.findByText('First Name');
await canvas.findByText('Last Name');
},
};

View File

@ -3,18 +3,14 @@ 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 { FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS } from '@/object-record/record-field/meta-types/input/constants/FirstNamePlaceholder';
import { LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS } from '@/object-record/record-field/meta-types/input/constants/LastNamePlaceholder';
import { isDoubleTextFieldEmpty } from '@/object-record/record-field/meta-types/input/utils/isDoubleTextFieldEmpty';
import {
FieldInputClickOutsideEvent,
FieldInputEvent,
} from './DateTimeFieldInput';
const FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS =
'First name';
const LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS =
'Last name';
type FullNameFieldInputProps = {
onClickOutside?: FieldInputClickOutsideEvent;
onEnter?: FieldInputEvent;

View File

@ -0,0 +1,2 @@
export const FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS =
'First name';

View File

@ -0,0 +1,2 @@
export const LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS =
'Last name';