fix(striker-ui): disable submit in StrikerInitForm until sub-forms are valid

main
Tsu-ba-me 2 years ago
parent 9f9f16dc93
commit 56100ee62b
  1. 153
      striker-ui/components/GeneralInitForm.tsx
  2. 34
      striker-ui/components/StrikerInitForm.tsx

@ -23,11 +23,7 @@ import OutlinedInputWithLabel, {
} from './OutlinedInputWithLabel'; } from './OutlinedInputWithLabel';
import pad from '../lib/pad'; import pad from '../lib/pad';
import SuggestButton from './SuggestButton'; import SuggestButton from './SuggestButton';
import { import { createTestInputFunction, testNotBlank } from '../lib/test_input';
createTestInputFunction,
testLength,
testNotBlank,
} from '../lib/test_input';
import { InputTestBatches } from '../types/TestInputFunction'; import { InputTestBatches } from '../types/TestInputFunction';
import { BodyText } from './Text'; import { BodyText } from './Text';
@ -51,6 +47,15 @@ const MAX_ORGANIZATION_PREFIX_LENGTH = 5;
const MIN_ORGANIZATION_PREFIX_LENGTH = 1; const MIN_ORGANIZATION_PREFIX_LENGTH = 1;
const MAX_HOST_NUMBER_LENGTH = 2; const MAX_HOST_NUMBER_LENGTH = 2;
const INPUT_COUNT = 7; const INPUT_COUNT = 7;
const INPUT_TEST_IDS = {
adminPassword: 'adminPassword',
confirmAdminPassword: 'confirmAdminPassword',
domainName: 'domainName',
hostName: 'hostName',
hostNumber: 'hostNumber',
organizationName: 'organizationName',
organizationPrefix: 'organizationPrefix',
};
const MAP_TO_ORGANIZATION_PREFIX_BUILDER: Record< const MAP_TO_ORGANIZATION_PREFIX_BUILDER: Record<
number, number,
@ -104,30 +109,28 @@ const MessageChildren: FC<FlexBoxProps> = ({ ...flexBoxProps }) => (
/> />
); );
const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>( const GeneralInitForm = forwardRef<
(generalInitFormProps, ref) => { GeneralInitFormForwardRefContent,
{ toggleSubmitDisabled?: ToggleSubmitDisabledFunction }
>(({ toggleSubmitDisabled }, ref) => {
const [helpMessage, setHelpMessage] = useState<ReactNode | undefined>(); const [helpMessage, setHelpMessage] = useState<ReactNode | undefined>();
const [ const [isShowOrganizationPrefixSuggest, setIsShowOrganizationPrefixSuggest] =
isShowOrganizationPrefixSuggest, useState<boolean>(false);
setIsShowOrganizationPrefixSuggest,
] = useState<boolean>(false);
const [isShowHostNameSuggest, setIsShowHostNameSuggest] = const [isShowHostNameSuggest, setIsShowHostNameSuggest] =
useState<boolean>(false); useState<boolean>(false);
const [isConfirmAdminPassword, setIsConfirmAdminPassword] = const [isConfirmAdminPassword, setIsConfirmAdminPassword] =
useState<boolean>(true); useState<boolean>(true);
const adminPasswordInputRef = useRef<InputForwardedRefContent<'string'>>( const adminPasswordInputRef = useRef<InputForwardedRefContent<'string'>>({});
{},
);
const confirmAdminPasswordInputRef = useRef< const confirmAdminPasswordInputRef = useRef<
InputForwardedRefContent<'string'> InputForwardedRefContent<'string'>
>({}); >({});
const organizationNameInputRef = useRef<InputForwardedRefContent<'string'>>( const organizationNameInputRef = useRef<InputForwardedRefContent<'string'>>(
{}, {},
); );
const organizationPrefixInputRef = useRef< const organizationPrefixInputRef = useRef<InputForwardedRefContent<'string'>>(
InputForwardedRefContent<'string'> {},
>({}); );
const domainNameInputRef = useRef<InputForwardedRefContent<'string'>>({}); const domainNameInputRef = useRef<InputForwardedRefContent<'string'>>({});
const hostNumberInputRef = useRef<InputForwardedRefContent<'number'>>({}); const hostNumberInputRef = useRef<InputForwardedRefContent<'number'>>({});
const hostNameInputRef = useRef<InputForwardedRefContent<'string'>>({}); const hostNameInputRef = useRef<InputForwardedRefContent<'string'>>({});
@ -166,8 +169,9 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
const inputTests: InputTestBatches = useMemo( const inputTests: InputTestBatches = useMemo(
() => ({ () => ({
adminPassword: { [INPUT_TEST_IDS.adminPassword]: {
defaults: { defaults: {
getValue: () => adminPasswordInputRef.current.getValue?.call(null),
onSuccess: () => { onSuccess: () => {
setAdminPasswordInputMessage(undefined); setAdminPasswordInputMessage(undefined);
}, },
@ -191,8 +195,10 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
{ test: testNotBlank }, { test: testNotBlank },
], ],
}, },
confirmAdminPassword: { [INPUT_TEST_IDS.confirmAdminPassword]: {
defaults: { defaults: {
getValue: () =>
confirmAdminPasswordInputRef.current.getValue?.call(null),
onSuccess: () => { onSuccess: () => {
setConfirmAdminPasswordInputMessage(undefined); setConfirmAdminPasswordInputMessage(undefined);
}, },
@ -210,8 +216,9 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
{ test: testNotBlank }, { test: testNotBlank },
], ],
}, },
domainName: { [INPUT_TEST_IDS.domainName]: {
defaults: { defaults: {
getValue: () => domainNameInputRef.current.getValue?.call(null),
onSuccess: () => { onSuccess: () => {
setDomainNameInputMessage(undefined); setDomainNameInputMessage(undefined);
}, },
@ -222,8 +229,8 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
setDomainNameInputMessage({ setDomainNameInputMessage({
children: ( children: (
<MessageChildren> <MessageChildren>
Domain name can only contain lowercase alphanumeric, Domain name can only contain lowercase alphanumeric, hyphen
hyphen ({ms('-')}), and dot ({ms('.')}) characters. ({ms('-')}), and dot ({ms('.')}) characters.
</MessageChildren> </MessageChildren>
), ),
}); });
@ -233,8 +240,9 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
{ test: testNotBlank }, { test: testNotBlank },
], ],
}, },
hostName: { [INPUT_TEST_IDS.hostName]: {
defaults: { defaults: {
getValue: () => hostNameInputRef.current.getValue?.call(null),
onSuccess: () => { onSuccess: () => {
setHostNameInputMessage(undefined); setHostNameInputMessage(undefined);
}, },
@ -245,8 +253,8 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
setHostNameInputMessage({ setHostNameInputMessage({
children: ( children: (
<MessageChildren> <MessageChildren>
Host name can only contain lowercase alphanumeric, hyphen Host name can only contain lowercase alphanumeric, hyphen (
({ms('-')}), and dot ({ms('.')}) characters. {ms('-')}), and dot ({ms('.')}) characters.
</MessageChildren> </MessageChildren>
), ),
}); });
@ -256,8 +264,9 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
{ test: testNotBlank }, { test: testNotBlank },
], ],
}, },
hostNumber: { [INPUT_TEST_IDS.hostNumber]: {
defaults: { defaults: {
getValue: () => hostNumberInputRef.current.getValue?.call(null),
onSuccess: () => { onSuccess: () => {
setHostNumberInputMessage(undefined); setHostNumberInputMessage(undefined);
}, },
@ -274,11 +283,18 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
{ test: testNotBlank }, { test: testNotBlank },
], ],
}, },
organizationName: { [INPUT_TEST_IDS.organizationName]: {
defaults: {
getValue: () => organizationNameInputRef.current.getValue?.call(null),
},
tests: [{ test: testNotBlank }], tests: [{ test: testNotBlank }],
}, },
organizationPrefix: { [INPUT_TEST_IDS.organizationPrefix]: {
defaults: { defaults: {
getValue: () =>
organizationPrefixInputRef.current.getValue?.call(null),
max: MAX_ORGANIZATION_PREFIX_LENGTH,
min: MIN_ORGANIZATION_PREFIX_LENGTH,
onSuccess: () => { onSuccess: () => {
setOrganizationPrefixInputMessage(undefined); setOrganizationPrefixInputMessage(undefined);
}, },
@ -287,21 +303,12 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
{ {
onFailure: ({ max, min }) => { onFailure: ({ max, min }) => {
setOrganizationPrefixInputMessage({ setOrganizationPrefixInputMessage({
children: `Organization prefix must be ${min} to ${max} characters.`, children: `Organization prefix must be ${min} to ${max} lowercase alphanumeric characters.`,
});
},
test: testLength,
},
{
onFailure: () => {
setOrganizationPrefixInputMessage({
children:
'Organization prefix can only contain lowercase alphanumeric characters.',
}); });
}, },
test: ({ value }) => /^[a-z0-9]+$/.test(value as string), test: ({ max, min, value }) =>
RegExp(`^[a-z0-9]{${min},${max}}$`).test(value as string),
}, },
{ test: testNotBlank },
], ],
}, },
}), }),
@ -319,11 +326,14 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
() => createTestInputFunction(inputTests), () => createTestInputFunction(inputTests),
[inputTests], [inputTests],
); );
const testAllInputs = useCallback(
(...excludeTestIds: string[]) =>
testInput({ excludeTestIds, isIgnoreOnCallbacks: true }),
[testInput],
);
const populateOrganizationPrefixInput = useCallback( const populateOrganizationPrefixInput = useCallback(
({ ({
organizationName = organizationNameInputRef.current.getValue?.call( organizationName = organizationNameInputRef.current.getValue?.call(null),
null,
),
} = {}) => { } = {}) => {
const organizationPrefix = buildOrganizationPrefix(organizationName); const organizationPrefix = buildOrganizationPrefix(organizationName);
@ -380,9 +390,7 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
const populateOrganizationPrefixInputOnBlur: OutlinedInputWithLabelOnBlur = const populateOrganizationPrefixInputOnBlur: OutlinedInputWithLabelOnBlur =
useCallback(() => { useCallback(() => {
if (organizationPrefixInputRef.current.getIsChangedByUser?.call(null)) { if (organizationPrefixInputRef.current.getIsChangedByUser?.call(null)) {
setIsShowOrganizationPrefixSuggest( setIsShowOrganizationPrefixSuggest(isOrganizationPrefixPrereqFilled());
isOrganizationPrefixPrereqFilled(),
);
} else { } else {
populateOrganizationPrefixInput(); populateOrganizationPrefixInput();
} }
@ -437,6 +445,9 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
}} }}
inputLabelProps={{ isNotifyRequired: true }} inputLabelProps={{ isNotifyRequired: true }}
label="Organization name" label="Organization name"
onChange={() => {
toggleSubmitDisabled?.call(null, testAllInputs());
}}
onHelp={() => { onHelp={() => {
setHelpMessage( setHelpMessage(
buildHelpMessage( buildHelpMessage(
@ -480,13 +491,17 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
testInput({ testInput({
inputs: { inputs: {
organizationPrefix: { [INPUT_TEST_IDS.organizationPrefix]: {
max: MAX_ORGANIZATION_PREFIX_LENGTH, max: MAX_ORGANIZATION_PREFIX_LENGTH,
min: MIN_ORGANIZATION_PREFIX_LENGTH, min: MIN_ORGANIZATION_PREFIX_LENGTH,
value, value,
}, },
}, },
}); });
toggleSubmitDisabled?.call(
null,
testAllInputs(INPUT_TEST_IDS.organizationPrefix),
);
setIsShowOrganizationPrefixSuggest( setIsShowOrganizationPrefixSuggest(
isOrganizationPrefixPrereqFilled(), isOrganizationPrefixPrereqFilled(),
); );
@ -518,7 +533,13 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
inputLabelProps={{ isNotifyRequired: true }} inputLabelProps={{ isNotifyRequired: true }}
label="Striker #" label="Striker #"
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
testInput({ inputs: { hostNumber: { value } } }); testInput({
inputs: { [INPUT_TEST_IDS.hostNumber]: { value } },
});
toggleSubmitDisabled?.call(
null,
testAllInputs(INPUT_TEST_IDS.hostNumber),
);
}} }}
onHelp={() => { onHelp={() => {
setHelpMessage( setHelpMessage(
@ -547,7 +568,13 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
inputLabelProps={{ isNotifyRequired: true }} inputLabelProps={{ isNotifyRequired: true }}
label="Domain name" label="Domain name"
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
testInput({ inputs: { domainName: { value } } }); testInput({
inputs: { [INPUT_TEST_IDS.domainName]: { value } },
});
toggleSubmitDisabled?.call(
null,
testAllInputs(INPUT_TEST_IDS.domainName),
);
}} }}
onHelp={() => { onHelp={() => {
setHelpMessage( setHelpMessage(
@ -576,7 +603,13 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
inputLabelProps={{ isNotifyRequired: true }} inputLabelProps={{ isNotifyRequired: true }}
label="Host name" label="Host name"
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
testInput({ inputs: { hostName: { value } } }); testInput({
inputs: { [INPUT_TEST_IDS.hostName]: { value } },
});
toggleSubmitDisabled?.call(
null,
testAllInputs(INPUT_TEST_IDS.hostName),
);
setIsShowHostNameSuggest(isHostNamePrereqFilled()); setIsShowHostNameSuggest(isHostNamePrereqFilled());
}} }}
onHelp={() => { onHelp={() => {
@ -621,7 +654,13 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
inputLabelProps={{ isNotifyRequired: true }} inputLabelProps={{ isNotifyRequired: true }}
label="Admin password" label="Admin password"
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
testInput({ inputs: { adminPassword: { value } } }); testInput({
inputs: { [INPUT_TEST_IDS.adminPassword]: { value } },
});
toggleSubmitDisabled?.call(
null,
testAllInputs(INPUT_TEST_IDS.adminPassword),
);
}} }}
onHelp={() => { onHelp={() => {
setHelpMessage( setHelpMessage(
@ -652,8 +691,14 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
label="Confirm password" label="Confirm password"
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
testInput({ testInput({
inputs: { confirmAdminPassword: { value } }, inputs: {
[INPUT_TEST_IDS.confirmAdminPassword]: { value },
},
}); });
toggleSubmitDisabled?.call(
null,
testAllInputs(INPUT_TEST_IDS.confirmAdminPassword),
);
}} }}
/> />
} }
@ -680,9 +725,9 @@ const GeneralInitForm = forwardRef<GeneralInitFormForwardRefContent>(
)} )}
</FlexBox> </FlexBox>
); );
}, });
);
GeneralInitForm.defaultProps = { toggleSubmitDisabled: undefined };
GeneralInitForm.displayName = 'GeneralInitForm'; GeneralInitForm.displayName = 'GeneralInitForm';
export type { GeneralInitFormForwardRefContent }; export type { GeneralInitFormForwardRefContent };

@ -1,4 +1,4 @@
import { FC, useMemo, useRef, useState } from 'react'; import { FC, useCallback, useMemo, useRef, useState } from 'react';
import ContainedButton from './ContainedButton'; import ContainedButton from './ContainedButton';
import FlexBox from './FlexBox'; import FlexBox from './FlexBox';
@ -16,6 +16,11 @@ import { HeaderText } from './Text';
const StrikerInitForm: FC = () => { const StrikerInitForm: FC = () => {
const [submitMessage, setSubmitMessage] = useState<Message | undefined>(); const [submitMessage, setSubmitMessage] = useState<Message | undefined>();
const [isDisableSubmit, setIsDisableSubmit] = useState<boolean>(true);
const [isGeneralInitFormValid, setIsGeneralInitFormValid] =
useState<boolean>(false);
const [isNetworkInitFormValid, setIsNetworkInitFormValid] =
useState<boolean>(false);
const [isSubmittingForm, setIsSubmittingForm] = useState<boolean>(false); const [isSubmittingForm, setIsSubmittingForm] = useState<boolean>(false);
const generalInitFormRef = useRef<GeneralInitFormForwardRefContent>({}); const generalInitFormRef = useRef<GeneralInitFormForwardRefContent>({});
@ -28,6 +33,7 @@ const StrikerInitForm: FC = () => {
) : ( ) : (
<FlexBox row sx={{ flexDirection: 'row-reverse' }}> <FlexBox row sx={{ flexDirection: 'row-reverse' }}>
<ContainedButton <ContainedButton
disabled={isDisableSubmit}
onClick={() => { onClick={() => {
setIsSubmittingForm(true); setIsSubmittingForm(true);
@ -57,17 +63,37 @@ const StrikerInitForm: FC = () => {
</ContainedButton> </ContainedButton>
</FlexBox> </FlexBox>
), ),
[isSubmittingForm], [isDisableSubmit, isSubmittingForm],
); );
const toggleSubmitDisabled = useCallback((...testResults: boolean[]) => {
setIsDisableSubmit(!testResults.every((testResult) => testResult));
}, []);
return ( return (
<Panel> <Panel>
<PanelHeader> <PanelHeader>
<HeaderText text="Initialize striker" /> <HeaderText text="Initialize striker" />
</PanelHeader> </PanelHeader>
<FlexBox> <FlexBox>
<GeneralInitForm ref={generalInitFormRef} /> <GeneralInitForm
<NetworkInitForm ref={networkInitFormRef} /> ref={generalInitFormRef}
toggleSubmitDisabled={(testResult) => {
if (testResult !== isGeneralInitFormValid) {
setIsGeneralInitFormValid(testResult);
toggleSubmitDisabled(testResult, isNetworkInitFormValid);
}
}}
/>
<NetworkInitForm
ref={networkInitFormRef}
toggleSubmitDisabled={(testResult) => {
if (testResult !== isNetworkInitFormValid) {
setIsNetworkInitFormValid(testResult);
toggleSubmitDisabled(isGeneralInitFormValid, testResult);
}
}}
/>
{submitMessage && ( {submitMessage && (
<MessageBox <MessageBox
{...submitMessage} {...submitMessage}

Loading…
Cancel
Save