From 8f18bb47e018bcc11ff2348f86ca9ee6d1e0b30e Mon Sep 17 00:00:00 2001 From: Emile Bex Date: Tue, 8 Aug 2023 19:01:54 +0200 Subject: [PATCH] continue --- .../forms/FormSchema/FormSchema.types.ts | 132 ++++++++++++------ .../forms/FormSchema/FormSchema.utils.ts | 16 ++- src/components/forms/FormWithValidation.tsx | 32 +++-- src/components/forms/fields/GenericField.tsx | 10 +- .../forms/schemas/formResetPassword.ts | 90 ++++++------ src/utils/Types.ts | 8 ++ 6 files changed, 182 insertions(+), 106 deletions(-) diff --git a/src/components/forms/FormSchema/FormSchema.types.ts b/src/components/forms/FormSchema/FormSchema.types.ts index 28065583e..542458aa7 100644 --- a/src/components/forms/FormSchema/FormSchema.types.ts +++ b/src/components/forms/FormSchema/FormSchema.types.ts @@ -1,6 +1,6 @@ import { RadioTypes } from 'src/components/utils/Inputs/Radio/Radio.types'; import { FilterConstant } from 'src/constants'; -import { AnyToFix } from 'src/utils/Types'; +import { AnyToFix, StrictUnion } from 'src/utils/Types'; const FormComponents = { DATEPICKER: 'datepicker', @@ -20,8 +20,10 @@ const FormComponents = { MULTIPLE_FIELDS: 'multiple-fields', } as const; +/* export type FormComponent = (typeof FormComponents)[keyof typeof FormComponents]; +*/ export type FieldValue = | string @@ -36,8 +38,7 @@ type MultiFilterConstant = M extends true ? FilterConstant : FilterConstant | FilterConstant[]; -interface FormComponentValues - extends Record { +export interface FormComponentValues { [FormComponents.DATEPICKER]: string; [FormComponents.TEXT_INPUT]: string; [FormComponents.TEL_INPUT]: string; @@ -51,6 +52,24 @@ interface FormComponentValues [FormComponents.RADIO_ASYNC]: string | number; } +// TODO remove boolean +export type FormComponent = keyof FormComponentValues; + +export type SchemaComponents = { + [K in string]: FormComponent; +}; + +export type FormFieldName = keyof S & string; + +type FormComponentValue< + S extends SchemaComponents, + FieldName extends FormFieldName +> = FormComponentValues[S[FieldName]]; + +export type SchemaValidation = { + [K in FormFieldName]: FormComponentValue; +}; + export const TextInputComponents = [ FormComponents.DATEPICKER, FormComponents.TEXT_INPUT, @@ -98,40 +117,51 @@ type InputComponent = | SelectComponent | SelectRequestComponent; -// export type FormFieldName = keyof S & string; - -interface FormFieldCommonProperties { - id: S; - name: S; +interface FormFieldCommonProperties { + id: FormFieldName; + name: FormFieldName; + component: FormComponentValue>; } -// export type GetValueType = (name: FormFieldName) => S[FormFieldName]; +export type GetValueType< + S extends SchemaComponents, + N extends FormFieldName +> = (name: N) => SchemaValidation[N]; -export type GetValueType = (name: string) => AnyToFix; +// export type GetValueType> = (name: string) => AnyToFix; -interface Rule { - method: (fieldValue: FormComponentValues[T], fieldValues: S) => boolean; +interface Rule { + method: ( + fieldValue: FormComponentValues[T], + fieldValues: AnyToFix + ) => boolean; args?: AnyToFix[]; message: string; } interface FormFieldInputCommonProperties< T extends InputComponent, - S, + S extends SchemaComponents, M extends boolean = boolean > extends FormFieldCommonProperties { isRequired?: boolean; - rules?: Rule[]; - title: string | JSX.Element | ((getValue: GetValueType) => string); + rules?: Rule[]; + title: + | string + | JSX.Element + | ((getValue: GetValueType>) => string); disabled?: boolean; - disable?: (getValue: GetValueType) => boolean; + disable?: (getValue: GetValueType>) => boolean; hidden?: boolean; - hide?: (getValue: GetValueType, fieldOptions?: AnyToFix) => boolean; + hide?: ( + getValue: GetValueType>, + fieldOptions?: AnyToFix + ) => boolean; placeholder?: string; showLabel?: boolean; } -export interface FormFieldTextInput +export interface FormFieldTextInput extends FormFieldInputCommonProperties { component: TextInputComponent; type?: 'text' | 'email' | 'password'; @@ -142,100 +172,120 @@ export interface FormFieldTextInput max?: string; } -export interface FormFieldCheckBox +export interface FormFieldCheckBox extends FormFieldInputCommonProperties { component: CheckBoxComponent; } -export interface FormFieldSelect +export interface FormFieldSelect extends FormFieldInputCommonProperties { component: SelectComponent; - dynamicFilter?: (getValue: GetValueType) => string; + dynamicFilter?: (getValue: GetValueType>) => string; fieldsToReset?: string[]; options?: FilterConstant[]; loadOptions?: ( callback: (options: FilterConstant[] | RadioTypes[]) => void, inputValue?: string, - getValue?: GetValueType + getValue?: GetValueType> ) => Promise | void; errorMessage?: string; limit?: number; } -export interface FormFieldSelectRequestCommon - extends FormFieldInputCommonProperties { +export interface FormFieldSelectRequestCommon< + S extends SchemaComponents, + M extends boolean +> extends FormFieldInputCommonProperties { component: SelectRequestComponent; fieldsToReset?: string[]; options?: FilterConstant[]; loadOptions?: ( callback: (options: FilterConstant[] | RadioTypes[]) => void, inputValue?: string, - getValue?: GetValueType + getValue?: GetValueType> ) => Promise | void; openMenuOnClick?: boolean; } // TODO fix type depending on is Multi -interface FormFieldSelectRequestMulti +interface FormFieldSelectRequestMulti extends FormFieldSelectRequestCommon { isMulti: true; } -interface FormFieldSelectRequestSingle +interface FormFieldSelectRequestSingle extends FormFieldSelectRequestCommon { isMulti: false; } -interface FormFieldSelectRequestMethod +interface FormFieldSelectRequestMethod extends FormFieldSelectRequestCommon { isMulti: (getValue: (name: string) => AnyToFix) => boolean; } -type FormFieldSelectRequestOmit = Omit< +type FormFieldSelectRequestOmit = Omit< FormFieldSelectRequestCommon, 'isMulti' >; -export type FormFieldSelectRequest = +export type FormFieldSelectRequest = | FormFieldSelectRequestMulti | FormFieldSelectRequestSingle | FormFieldSelectRequestOmit | FormFieldSelectRequestMethod; -export type FormFieldInput = +export type FormFieldInput = | FormFieldTextInput | FormFieldCheckBox | FormFieldSelect | FormFieldSelectRequest; -export interface FormFieldText extends FormFieldCommonProperties { - title: string | ((getValue: GetValueType) => string); +export interface FormFieldText + extends FormFieldCommonProperties { + title: string | ((getValue: GetValueType>) => string); component: TextComponent; - hide?: (getValue: GetValueType, fieldOptions?: AnyToFix) => boolean; + hide?: ( + getValue: GetValueType>, + fieldOptions?: AnyToFix + ) => boolean; hidden?: boolean; } -export interface FormFieldMultiple extends FormFieldCommonProperties { +export interface FormFieldMultiple + extends FormFieldCommonProperties { action: string; component: MultipleComponent; fields: FormFieldInput[]; - hide?: (getValue: GetValueType) => boolean; + hide?: (getValue: GetValueType>) => boolean; hidden?: boolean; } -export interface FormFieldGroup extends FormFieldCommonProperties { +export interface FormFieldGroup + extends FormFieldCommonProperties { component: GroupComponent; fields: (FormFieldInput | FormFieldText)[]; - hide?: (getValue: GetValueType) => boolean; + hide?: (getValue: GetValueType>) => boolean; hidden?: boolean; } -export type FormField = +export type FormField = | FormFieldInput | FormFieldText | FormFieldMultiple | FormFieldGroup; -export interface FormSchema { +export interface FormSchema { id: string; - fields: FormField[]; + fields: FormField[]; } + +export type FormSchemaValidation = R extends FormSchema< + infer S extends SchemaComponents +> + ? SchemaValidation + : never; + +export type FormSchemaComponent = R extends FormSchema< + infer S extends SchemaComponents +> + ? S + : never; diff --git a/src/components/forms/FormSchema/FormSchema.utils.ts b/src/components/forms/FormSchema/FormSchema.utils.ts index 7883bb649..c3f80b780 100644 --- a/src/components/forms/FormSchema/FormSchema.utils.ts +++ b/src/components/forms/FormSchema/FormSchema.utils.ts @@ -6,13 +6,16 @@ import { FormFieldCheckBox, FormFieldGroup, FormFieldMultiple, - FormFieldSelect, FormFieldSelectRequest, + FormFieldSelect, + FormFieldSelectRequest, FormFieldText, FormFieldTextInput, + FormSchema, GroupComponent, GroupComponents, MultipleComponent, MultipleComponents, + SchemaComponents, SelectComponent, SelectComponents, SelectRequestComponent, @@ -20,8 +23,15 @@ import { TextComponent, TextComponents, TextInputComponent, - TextInputComponents -} from "./FormSchema.types"; + TextInputComponents, +} from './FormSchema.types'; + +export function createFormSchema( + schemaComponents: T, + formSchema: FormSchema +) { + return formSchema; +} export function isFormFieldTextInput( field: FormField diff --git a/src/components/forms/FormWithValidation.tsx b/src/components/forms/FormWithValidation.tsx index 8bb02a9fa..a687f1a5b 100644 --- a/src/components/forms/FormWithValidation.tsx +++ b/src/components/forms/FormWithValidation.tsx @@ -21,25 +21,30 @@ import { isFormFieldMultiple, isFormFieldSelect, isFormFieldText, -} from './FormSchema'; + SchemaComponents, SchemaValidation, FormSchemaValidation, FormFieldName, FormSchemaComponent +} from "./FormSchema"; import { StyledForm } from './Forms.styles'; +import { formAddUser } from "./schemas/formAddUser"; +import { formResetPassword } from "./schemas/formResetPassword"; -interface FormWithValidationProps { +interface FormWithValidationProps> { defaultValues?: AnyToFix; // to be typed onCancel?: () => void; onSubmit: (arg1: AnyToFix, arg2: AnyToFix) => void; // to be typed onError?: (any) => void; - formSchema: FormSchema; + formSchema: S; submitText?: string; cancelText?: string; enterToSubmit?: boolean; + ref?: React.Ref<{ resetForm: () => void }>; } -export const FormWithValidation = forwardRef< - { resetForm: () => void }, - FormWithValidationProps ->( - ( +type Test = FormWithValidationProps['formSchema'] +type Name = FormFieldName> +type Test3 = FormSchemaValidation['confirmPassword'] + + +export function FormWithValidation>( { formSchema, defaultValues = {}, @@ -49,9 +54,10 @@ export const FormWithValidation = forwardRef< onCancel, enterToSubmit = false, onError, - }: FormWithValidationProps, - ref - ) => { + ref, + }: FormWithValidationProps){ + + const { id: formId, fields } = formSchema; const [error, setError] = useState(); @@ -65,7 +71,7 @@ export const FormWithValidation = forwardRef< ); const { handleSubmit, control, reset, getValues, resetField, watch } = - useForm({ + useForm>({ defaultValues, shouldUnregister: true, }); @@ -183,7 +189,7 @@ export const FormWithValidation = forwardRef< } return !shouldHideField ? ( - watch={watch} resetField={resetField} control={control} diff --git a/src/components/forms/fields/GenericField.tsx b/src/components/forms/fields/GenericField.tsx index a061c903a..e47723b35 100644 --- a/src/components/forms/fields/GenericField.tsx +++ b/src/components/forms/fields/GenericField.tsx @@ -8,12 +8,12 @@ import { import { ComponentException, - FormFieldInput, + FormFieldInput, FormSchema, FormSchemaValidation, GetValueType, isFormFieldSelect, isFormFieldSelectRequest, - isFormFieldTextInput, -} from '../FormSchema'; + isFormFieldTextInput +} from "../FormSchema"; import { CheckBox, DatePicker, @@ -31,7 +31,7 @@ import { CommonInputProps } from 'src/components/utils/Inputs/Inputs.types'; import { AnyToFix } from 'src/utils/Types'; -interface GenericFieldProps { +interface GenericFieldProps> { field: FormFieldInput; formId: string; getValue: GetValueType; @@ -42,7 +42,7 @@ interface GenericFieldProps { watch: UseFormWatch; } -export function GenericField({ +export function GenericField>({ field, formId, getValue, diff --git a/src/components/forms/schemas/formResetPassword.ts b/src/components/forms/schemas/formResetPassword.ts index 5195e9258..104623bd8 100644 --- a/src/components/forms/schemas/formResetPassword.ts +++ b/src/components/forms/schemas/formResetPassword.ts @@ -1,48 +1,50 @@ import { passwordStrength } from 'check-password-strength'; -import { FormSchema } from '../FormSchema'; +import { createFormSchema } from '../FormSchema'; -interface Schema { - newPassword: string; - confirmPassword: string; -} -export const formResetPassword: FormSchema = { - id: 'form-reset-pwd', - fields: [ - { - id: 'newPassword', - name: 'newPassword', - type: 'password', - component: 'text-input', - title: 'Nouveau mot de passe*', - isRequired: true, - rules: [ - { - method: (fieldValue) => { - return passwordStrength(fieldValue).id >= 2; +export const formResetPassword = createFormSchema( + { + newPassword: 'text-input', + confirmPassword: 'text-input', + }, + { + id: 'form-reset-pwd', + fields: [ + { + id: 'newPassword', + name: 'newPassword', + type: 'password', + component: 'text-input', + title: 'Nouveau mot de passe*', + isRequired: true, + rules: [ + { + method: (fieldValue) => { + return passwordStrength(fieldValue).id >= 2; + }, + message: 'Doit répondre aux critères ci-dessus', }, - message: 'Doit répondre aux critères ci-dessus', - }, - ], - }, - { - id: 'confirmPassword', - name: 'confirmPassword', - type: 'password', - component: 'text-input', - title: 'Confirmation du mot de passe*', - isRequired: true, - rules: [ - { - method: (fieldValue) => passwordStrength(fieldValue).id >= 2, - message: 'Doit répondre aux critères ci-dessus', - }, - { - method: (fieldValue, fieldValues) => { - return fieldValues.newPassword === fieldValue; + ], + }, + { + id: 'confirmPassword', + name: 'confirmPassword', + type: 'password', + component: 'text-input', + title: 'Confirmation du mot de passe*', + isRequired: true, + rules: [ + { + method: (fieldValue) => passwordStrength(fieldValue).id >= 2, + message: 'Doit répondre aux critères ci-dessus', }, - message: 'Les deux mots de passe ne correspondent pas', - }, - ], - }, - ], -}; + { + method: (fieldValue, fieldValues) => { + return fieldValues.newPassword === fieldValue; + }, + message: 'Les deux mots de passe ne correspondent pas', + }, + ], + }, + ], + } +); diff --git a/src/utils/Types.ts b/src/utils/Types.ts index 2e397e05e..11cc9b621 100644 --- a/src/utils/Types.ts +++ b/src/utils/Types.ts @@ -1,3 +1,11 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ export type AnyToFix = any; export type AnyCantFix = any; + +type UnionKeys = T extends any ? keyof T : never; + +type StrictUnionHelper = T extends any + ? T & Partial, keyof T>, never>> + : never; + +export type StrictUnion = StrictUnionHelper;