From 95ce723b861b639f1b9daffc5d83a5c7b35161c0 Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Tue, 4 Apr 2023 23:57:25 -0400 Subject: [PATCH] fix(striker-ui): revise GateForm to use form event --- striker-ui/components/GateForm.tsx | 367 +++++++++------------- striker-ui/components/PrepareHostForm.tsx | 23 +- striker-ui/types/GateForm.d.ts | 11 +- 3 files changed, 164 insertions(+), 237 deletions(-) diff --git a/striker-ui/components/GateForm.tsx b/striker-ui/components/GateForm.tsx index b72de4cf..49df89a1 100644 --- a/striker-ui/components/GateForm.tsx +++ b/striker-ui/components/GateForm.tsx @@ -1,7 +1,6 @@ -import { SxProps, Theme } from '@mui/material'; +import { Box, BoxProps, SxProps, Theme } from '@mui/material'; import { forwardRef, - useCallback, useImperativeHandle, useMemo, useRef, @@ -17,26 +16,22 @@ import InputWithRef, { InputForwardedRefContent } from './InputWithRef'; import MessageGroup, { MessageGroupForwardedRefContent } from './MessageGroup'; import OutlinedInputWithLabel from './OutlinedInputWithLabel'; import Spinner from './Spinner'; -import { - buildPeacefulStringTestBatch, - createTestInputFunction, -} from '../lib/test_input'; +import { buildPeacefulStringTestBatch } from '../lib/test_input'; +import useFormUtils from '../hooks/useFormUtils'; const INPUT_ROOT_SX: SxProps = { width: '100%' }; -const IT_IDS = { - identifier: 'identifier', - passphrase: 'passphrase', -}; -const MESSAGE_KEY: GateFormMessageKey = { - accessError: 'accessError', - identifierInputError: 'identifierInputError', - passphraseInputError: 'passphraseInputError', -}; + +const INPUT_ID_PREFIX_GATE = 'gate-input'; + +const INPUT_ID_GATE_ID = `${INPUT_ID_PREFIX_GATE}-credential-id`; +const INPUT_ID_GATE_PASSPHRASE = `${INPUT_ID_PREFIX_GATE}-credential-passphrase`; + +const MSG_ID_GATE_ACCESS = 'access'; const GateForm = forwardRef( ( { - allowSubmit: isAllowSubmit = true, + formContainer: isFormContainer = true, gridProps: { columns: gridColumns = { xs: 1, sm: 2 }, layout, @@ -49,7 +44,8 @@ const GateForm = forwardRef( inputProps: identifierInputProps, ...restIdentifierOutlinedInputWithLabelProps } = {}, - identifierInputTestBatchBuilder: overwriteIdentifierInputTestBatch, + identifierInputTestBatchBuilder: + buildIdentifierInputTestBatch = buildPeacefulStringTestBatch, onIdentifierBlurAppend, onSubmit, onSubmitAppend, @@ -72,151 +68,83 @@ const GateForm = forwardRef( const inputPassphraseRef = useRef>({}); const messageGroupRef = useRef({}); - const [isInputIdentifierValid, setIsInputIdentifierValid] = - useState(false); - const [isInputPassphraseValid, setIsInputPassphraseValid] = - useState(false); const [isSubmitting, setIsSubmitting] = useState(false); - const setAccessErrorMessage: GateFormMessageSetter = useCallback( - (message?) => { - messageGroupRef.current.setMessage?.call( - null, - MESSAGE_KEY.accessError, - message, - ); - }, - [], - ); - const setIdentifierInputErrorMessage: GateFormMessageSetter = useCallback( - (message?) => { - messageGroupRef.current.setMessage?.call( - null, - MESSAGE_KEY.identifierInputError, - message, - ); - }, - [], - ); - const setPassphraseInputErrorMessage: GateFormMessageSetter = useCallback( - (message?) => { - messageGroupRef.current.setMessage?.call( - null, - MESSAGE_KEY.passphraseInputError, - message, - ); - }, - [], + const formUtils = useFormUtils( + [INPUT_ID_GATE_ID, INPUT_ID_GATE_PASSPHRASE], + messageGroupRef, ); + const { + buildFinishInputTestBatchFunction, + buildInputFirstRenderFunction, + buildInputUnmountFunction, + isFormInvalid, + setMessage, + } = formUtils; - const messagesGroupSxDisplay = useMemo( - () => (isAllowSubmit ? undefined : 'none'), - [isAllowSubmit], - ); - const identifierInputTestBatch = useMemo( - () => - overwriteIdentifierInputTestBatch?.call( - null, - setIdentifierInputErrorMessage, - inputIdentifierRef.current, - ) ?? - buildPeacefulStringTestBatch( - identifierLabel, - () => { - setIdentifierInputErrorMessage(); - }, - { getValue: inputIdentifierRef.current.getValue }, - (message) => { - setIdentifierInputErrorMessage({ - children: message, - type: 'warning', - }); - }, - ), - [ - identifierLabel, - overwriteIdentifierInputTestBatch, - setIdentifierInputErrorMessage, - ], - ); - const inputTests: InputTestBatches = useMemo( - () => ({ - [IT_IDS.identifier]: identifierInputTestBatch, - [IT_IDS.passphrase]: buildPeacefulStringTestBatch( - passphraseLabel, - () => { - setPassphraseInputErrorMessage(); - }, - { getValue: inputPassphraseRef.current.getValue }, - (message) => { - setPassphraseInputErrorMessage({ - children: message, - type: 'warning', - }); - }, - ), - }), - [ - identifierInputTestBatch, - passphraseLabel, - setPassphraseInputErrorMessage, - ], - ); - const submitHandler: ContainedButtonProps['onClick'] = useMemo( + const submitHandler: DivFormEventHandler = useMemo( () => onSubmit ?? ((...args) => { - setAccessErrorMessage(); + const { 0: event } = args; + + event.preventDefault(); + + setMessage(MSG_ID_GATE_ACCESS); setIsSubmitting(true); onSubmitAppend?.call( null, inputIdentifierRef.current, inputPassphraseRef.current, - setAccessErrorMessage, + (message?) => { + setMessage(MSG_ID_GATE_ACCESS, message); + }, setIsSubmitting, messageGroupRef.current, ...args, ); }), - [onSubmit, onSubmitAppend, setAccessErrorMessage], + [onSubmit, onSubmitAppend, setMessage], ); + const submitElement = useMemo( () => isSubmitting ? ( ) : ( - + {submitLabel} ), - [ - isInputIdentifierValid, - isInputPassphraseValid, - isSubmitting, - submitHandler, - submitLabel, - ], - ); - const submitGrid = useMemo( - () => - isAllowSubmit - ? { - children: submitElement, - sm: 2, - } - : undefined, - [isAllowSubmit, submitElement], + [isFormInvalid, isSubmitting, submitLabel], ); - const testInput = useMemo( - () => createTestInputFunction(inputTests), - [inputTests], - ); + const submitAreaGridLayout = useMemo(() => { + const result: GridLayout = {}; + + if (isFormContainer) { + result['gate-cell-message-group'] = { + children: , + sm: 2, + }; + result['gate-cell-submit'] = { children: submitElement, sm: 2 }; + } + + return result; + }, [isFormContainer, submitElement]); + + const containerProps = useMemo(() => { + const result: BoxProps = {}; + + if (isFormContainer) { + result.component = 'form'; + result.onSubmit = submitHandler; + } + + return result; + }, [isFormContainer, submitHandler]); useImperativeHandle(ref, () => ({ get: () => ({ @@ -232,90 +160,105 @@ const GateForm = forwardRef( })); return ( - { - const { - target: { value }, - } = event; - - const valid = testInput({ - inputs: { [IT_IDS.identifier]: { value } }, - }); - setIsInputIdentifierValid(valid); - - onIdentifierBlurAppend?.call(null, event); - }, - onFocus: () => { - setIdentifierInputErrorMessage(); - }, - ...identifierInputProps, - }} - label={identifierLabel} - {...restIdentifierOutlinedInputWithLabelProps} - /> - } - ref={inputIdentifierRef} - /> - ), - }, - 'credential-passphrase': { - children: ( - { - const valid = testInput({ - inputs: { [IT_IDS.passphrase]: { value } }, - }); - setIsInputPassphraseValid(valid); - }, - onFocus: () => { - setPassphraseInputErrorMessage(); - }, - type: INPUT_TYPES.password, - ...passphraseInputProps, - }} - label={passphraseLabel} - {...restPassphraseOutlinedInputWithLabelProps} - /> - } - ref={inputPassphraseRef} - /> - ), - }, - 'credential-message-group': { - children: , - sm: 2, - sx: { display: messagesGroupSxDisplay }, - }, - 'credential-submit': submitGrid, - }} - spacing={gridSpacing} - {...restGridProps} - /> + + + } + inputTestBatch={buildIdentifierInputTestBatch( + identifierLabel, + () => { + setMessage(INPUT_ID_GATE_ID); + }, + { + onFinishBatch: + buildFinishInputTestBatchFunction(INPUT_ID_GATE_ID), + }, + (message) => { + setMessage(INPUT_ID_GATE_ID, { children: message }); + }, + )} + onBlurAppend={(...args) => { + onIdentifierBlurAppend?.call(null, ...args); + }} + onFirstRender={buildInputFirstRenderFunction( + INPUT_ID_GATE_ID, + )} + onUnmount={buildInputUnmountFunction(INPUT_ID_GATE_ID)} + ref={inputIdentifierRef} + required + /> + ), + }, + 'gate-input-cell-credential-passphrase': { + children: ( + + } + inputTestBatch={buildPeacefulStringTestBatch( + passphraseLabel, + () => { + setMessage(INPUT_ID_GATE_PASSPHRASE); + }, + { + onFinishBatch: buildFinishInputTestBatchFunction( + INPUT_ID_GATE_PASSPHRASE, + ), + }, + (message) => { + setMessage(INPUT_ID_GATE_PASSPHRASE, { + children: message, + }); + }, + )} + onFirstRender={buildInputFirstRenderFunction( + INPUT_ID_GATE_PASSPHRASE, + )} + onUnmount={buildInputUnmountFunction( + INPUT_ID_GATE_PASSPHRASE, + )} + ref={inputPassphraseRef} + required + /> + ), + }, + ...submitAreaGridLayout, + }} + spacing={gridSpacing} + {...restGridProps} + /> + ); }, ); GateForm.displayName = 'GateForm'; +export { INPUT_ID_GATE_ID, INPUT_ID_GATE_PASSPHRASE }; + export default GateForm; diff --git a/striker-ui/components/PrepareHostForm.tsx b/striker-ui/components/PrepareHostForm.tsx index 8dc1cbea..7f416cfd 100644 --- a/striker-ui/components/PrepareHostForm.tsx +++ b/striker-ui/components/PrepareHostForm.tsx @@ -58,7 +58,6 @@ const PrepareHostForm: FC = () => { const { protect } = useProtect(); const confirmDialogRef = useRef({}); - const gateFormRef = useRef({}); const inputEnterpriseKeyRef = useRef>({}); const inputHostNameRef = useRef>({}); const inputRedhatPassword = useRef>({}); @@ -199,7 +198,7 @@ const PrepareHostForm: FC = () => { const accessSection = useMemo( () => ( { }, }, }} - identifierInputTestBatchBuilder={(setMessage) => - buildIPAddressTestBatch( - HOST_IP_LABEL, - () => { - setMessage(); - }, - undefined, - (message) => { - setMessage({ children: message, type: 'warning' }); - }, - ) - } + identifierInputTestBatchBuilder={buildIPAddressTestBatch} identifierLabel={HOST_IP_LABEL} onIdentifierBlurAppend={({ target: { value } }) => { if (connectedHostIPAddress) { @@ -291,17 +279,16 @@ const PrepareHostForm: FC = () => { } }, ) - .catch((error) => { - const errorMessage = handleAPIError(error); + .catch((apiError) => { + const emsg = handleAPIError(apiError); - setMessage?.call(null, errorMessage); + setMessage?.call(null, emsg); }) .finally(() => { setIsSubmitting(false); }); }} passphraseLabel="Host root password" - ref={gateFormRef} submitLabel="Test access" /> ), diff --git a/striker-ui/types/GateForm.d.ts b/striker-ui/types/GateForm.d.ts index 5505c16b..f0f833e5 100644 --- a/striker-ui/types/GateForm.d.ts +++ b/striker-ui/types/GateForm.d.ts @@ -15,21 +15,18 @@ type GateFormSubmitHandler = ( setMessage: GateFormMessageSetter, setIsSubmitting: GateFormSubmittingSetter, messageGroupContent: import('../components/MessageGroup').MessageGroupForwardedRefContent, - ...args: Parameters + ...args: Parameters ) => void; type GateFormOptionalProps = { - allowSubmit?: boolean; + formContainer?: boolean; gridProps?: Partial; identifierOutlinedInputWithLabelProps?: Partial< import('../components/OutlinedInputWithLabel').OutlinedInputWithLabelProps >; - identifierInputTestBatchBuilder?: ( - setMessage: GateFormMessageSetter, - identifierContent: import('../components/InputWithRef').InputForwardedRefContent<'string'>, - ) => ReturnType; + identifierInputTestBatchBuilder?: BuildInputTestBatchFunction; onIdentifierBlurAppend?: import('../components/OutlinedInput').OutlinedInputProps['onBlur']; - onSubmit?: ContainedButtonProps['onClick']; + onSubmit?: DivFormEventHandler; onSubmitAppend?: GateFormSubmitHandler; passphraseOutlinedInputWithLabelProps?: Partial< import('../components/OutlinedInputWithLabel').OutlinedInputWithLabelProps