@sinchsmb/ui-kit
Version:
UI kit for SinchSMB frontend
105 lines (96 loc) • 3.15 kB
text/typescript
import { useField } from 'formik';
import { useCallback } from 'react';
import type { Except } from 'type-fest';
import { FormFieldProps } from '../../form/FormField/FormField';
import { GeneralFormControlProps } from '../../form/types';
import { useId } from '../../hooks/useId';
import { CommonFormikProps } from '../common/types';
/** Result of {@link useFormikFieldsProps} */
export interface UseFormikFieldsPropsResult<
Value,
OnChangeValue,
ControlProps extends GeneralFormControlProps<any, any>,
> {
/** Auto-generated field identificator */
id: string;
/** Props that should be used in {@link FormField} */
formFieldProps: Omit<FormFieldProps, 'children' | 'id' | 'direction' | 'ariaDescribedBy'>;
/** Props that should be used in form control that expect {@link GeneralFormControlProps}. */
controlProps: Except<
ControlProps & GeneralFormControlProps<Value, OnChangeValue>,
'className' | 'id' | 'testId'
>;
}
/**
* Return general props for {@link FormField} and some form control that expect {@link GeneralFormControlProps}
* based on current formik field state.
*/
export function useFormikFieldsProps<
ControlProps extends GeneralFormControlProps<any, any>,
Value = ControlProps extends GeneralFormControlProps<infer T, any> ? T : never,
OnChange = (ControlProps extends GeneralFormControlProps<any, infer T> ? T : unknown) extends unknown
? Value
: ControlProps extends GeneralFormControlProps<any, infer T>
? T
: never,
>(props: CommonFormikProps<OnChange>): UseFormikFieldsPropsResult<Value, OnChange, ControlProps> {
const {
disabled,
description,
requirement,
hintText,
onHintClick,
name,
label,
noVisualLabel,
className,
onChange,
testId,
ariaDescribedBy,
...otherProps
} = props;
const [field, meta, helpers] = useField(name);
const id = useId();
const errorMessageId = useId();
const descriptionMessageId = useId();
const handleOnChange = useCallback(
(value: OnChange) => {
if (onChange) {
onChange(value);
}
helpers.setValue(value);
},
[helpers, onChange],
);
const handleOnBlur = useCallback(() => {
helpers.setTouched(true);
}, [helpers]);
return {
id,
formFieldProps: {
errorMessageId: meta.error ? errorMessageId : undefined,
descriptionMessageId: !!description ? descriptionMessageId : undefined,
label: noVisualLabel ? undefined : label,
description,
requirement,
hintText,
onHintClick,
className,
error: meta.error ? meta.error : undefined,
testId,
},
controlProps: {
...otherProps,
value: field.value,
ariaLabel: noVisualLabel ? label : undefined,
ariaErrorMessage: meta.error ? errorMessageId : undefined,
ariaDescribedBy: ariaDescribedBy ?? (!!description ? descriptionMessageId : undefined),
ariaInvalid: !!meta.error,
ariaRequired: requirement ?? undefined,
required: requirement ?? undefined,
disabled: !!disabled,
onChange: handleOnChange,
onBlur: handleOnBlur,
} as ControlProps,
};
}