fix(striker-ui): add input test in InputWithRef to reduce duplication

main
Tsu-ba-me 2 years ago
parent fb16016ad0
commit 0156b7cd7e
  1. 105
      striker-ui/components/InputWithRef.tsx

@ -1,9 +1,12 @@
import { InputBaseProps } from '@mui/material';
import { import {
cloneElement, cloneElement,
ForwardedRef, ForwardedRef,
forwardRef, forwardRef,
ReactElement, ReactElement,
useEffect,
useImperativeHandle, useImperativeHandle,
useMemo,
useState, useState,
} from 'react'; } from 'react';
@ -11,16 +14,29 @@ import createInputOnChangeHandler, {
CreateInputOnChangeHandlerOptions, CreateInputOnChangeHandlerOptions,
MapToStateSetter, MapToStateSetter,
} from '../lib/createInputOnChangeHandler'; } from '../lib/createInputOnChangeHandler';
import { createTestInputFunction } from '../lib/test_input';
import useIsFirstRender from '../hooks/useIsFirstRender';
type InputWithRefTypeMap = Pick<MapToType, 'number' | 'string'>; type InputWithRefTypeMap = Pick<MapToType, 'number' | 'string'>;
type InputWithRefOptionalProps<TypeName extends keyof InputWithRefTypeMap> = { type InputWithRefOptionalPropsWithDefault<
TypeName extends keyof InputWithRefTypeMap,
> = {
createInputOnChangeHandlerOptions?: Omit< createInputOnChangeHandlerOptions?: Omit<
CreateInputOnChangeHandlerOptions<TypeName>, CreateInputOnChangeHandlerOptions<TypeName>,
'set' 'set'
>; >;
required?: boolean;
valueType?: TypeName | 'string'; valueType?: TypeName | 'string';
}; };
type InputWithRefOptionalPropsWithoutDefault = {
inputTestBatch?: InputTestBatch;
onFirstRender?: (args: { isRequired: boolean }) => void;
};
type InputWithRefOptionalProps<TypeName extends keyof InputWithRefTypeMap> =
InputWithRefOptionalPropsWithDefault<TypeName> &
InputWithRefOptionalPropsWithoutDefault;
type InputWithRefProps< type InputWithRefProps<
TypeName extends keyof InputWithRefTypeMap, TypeName extends keyof InputWithRefTypeMap,
@ -32,18 +48,22 @@ type InputWithRefProps<
type InputForwardedRefContent<TypeName extends keyof InputWithRefTypeMap> = { type InputForwardedRefContent<TypeName extends keyof InputWithRefTypeMap> = {
getIsChangedByUser?: () => boolean; getIsChangedByUser?: () => boolean;
getValue?: () => InputWithRefTypeMap[TypeName]; getValue?: () => InputWithRefTypeMap[TypeName];
isValid?: () => boolean;
setValue?: MapToStateSetter[TypeName]; setValue?: MapToStateSetter[TypeName];
}; };
const INPUT_TEST_ID = 'input';
const MAP_TO_INITIAL_VALUE: InputWithRefTypeMap = { const MAP_TO_INITIAL_VALUE: InputWithRefTypeMap = {
number: 0, number: 0,
string: '', string: '',
}; };
const INPUT_WITH_REF_DEFAULT_PROPS: Required< const INPUT_WITH_REF_DEFAULT_PROPS: Required<
InputWithRefOptionalProps<'string'> InputWithRefOptionalPropsWithDefault<'string'>
> = { > &
InputWithRefOptionalPropsWithoutDefault = {
createInputOnChangeHandlerOptions: {}, createInputOnChangeHandlerOptions: {},
required: false,
valueType: 'string', valueType: 'string',
}; };
@ -58,18 +78,67 @@ const InputWithRef = forwardRef(
...restCreateInputOnChangeHandlerOptions ...restCreateInputOnChangeHandlerOptions
} = INPUT_WITH_REF_DEFAULT_PROPS.createInputOnChangeHandlerOptions, } = INPUT_WITH_REF_DEFAULT_PROPS.createInputOnChangeHandlerOptions,
input, input,
inputTestBatch,
onFirstRender,
required: isRequired = INPUT_WITH_REF_DEFAULT_PROPS.required,
valueType = INPUT_WITH_REF_DEFAULT_PROPS.valueType, valueType = INPUT_WITH_REF_DEFAULT_PROPS.valueType,
}: InputWithRefProps<TypeName, InputComponent>, }: InputWithRefProps<TypeName, InputComponent>,
ref: ForwardedRef<InputForwardedRefContent<TypeName>>, ref: ForwardedRef<InputForwardedRefContent<TypeName>>,
) => { ) => {
const { const {
props: { onChange: initOnChange, value: initValue, ...restInitProps }, props: {
onBlur: initOnBlur,
onChange: initOnChange,
onFocus: initOnFocus,
value: initValue = MAP_TO_INITIAL_VALUE[valueType],
...restInitProps
},
} = input; } = input;
const [value, setValue] = useState<InputWithRefTypeMap[TypeName]>( const isFirstRender = useIsFirstRender();
initValue ?? MAP_TO_INITIAL_VALUE[valueType],
const [inputValue, setInputValue] = useState<InputWithRefTypeMap[TypeName]>(
initValue,
) as [InputWithRefTypeMap[TypeName], MapToStateSetter[TypeName]]; ) as [InputWithRefTypeMap[TypeName], MapToStateSetter[TypeName]];
const [isChangedByUser, setIsChangedByUser] = useState<boolean>(false); const [isChangedByUser, setIsChangedByUser] = useState<boolean>(false);
const [isInputValid, setIsInputValid] = useState<boolean>(false);
const testInput: TestInputFunction | undefined = useMemo(() => {
let result;
if (inputTestBatch) {
inputTestBatch.isRequired = isRequired;
result = createTestInputFunction({
[INPUT_TEST_ID]: inputTestBatch,
});
}
return result;
}, [inputTestBatch, isRequired]);
const onBlur = useMemo<InputBaseProps['onBlur']>(
() =>
initOnBlur ??
(testInput &&
(({ target: { value } }) => {
const isValid = testInput({
inputs: { [INPUT_TEST_ID]: { value } },
});
setIsInputValid(isValid);
})),
[initOnBlur, testInput],
);
const onFocus = useMemo<InputBaseProps['onFocus']>(
() =>
initOnFocus ??
(inputTestBatch &&
(() => {
inputTestBatch.defaults?.onSuccess?.call(null, { append: {} });
})),
[initOnFocus, inputTestBatch],
);
const onChange = createInputOnChangeHandler<TypeName>({ const onChange = createInputOnChangeHandler<TypeName>({
postSet: (...args) => { postSet: (...args) => {
@ -77,22 +146,36 @@ const InputWithRef = forwardRef(
initOnChange?.call(null, ...args); initOnChange?.call(null, ...args);
postSetAppend?.call(null, ...args); postSetAppend?.call(null, ...args);
}, },
set: setValue, set: setInputValue,
setType: valueType, setType: valueType,
...restCreateInputOnChangeHandlerOptions, ...restCreateInputOnChangeHandlerOptions,
}); });
useEffect(() => {
if (isFirstRender) {
onFirstRender?.call(null, { isRequired });
}
}, [isFirstRender, isRequired, onFirstRender]);
useImperativeHandle( useImperativeHandle(
ref, ref,
() => ({ () => ({
getIsChangedByUser: () => isChangedByUser, getIsChangedByUser: () => isChangedByUser,
getValue: () => value, getValue: () => inputValue,
setValue, isValid: () => isInputValid,
setValue: setInputValue,
}), }),
[isChangedByUser, value], [inputValue, isChangedByUser, isInputValid],
); );
return cloneElement(input, { ...restInitProps, onChange, value }); return cloneElement(input, {
...restInitProps,
onBlur,
onChange,
onFocus,
required: isRequired,
value: inputValue,
});
}, },
); );

Loading…
Cancel
Save