-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split the Porting Embed Form into a Wizard (#4)
- Check what's the next step in porting - Split the porting form up into 4 parts: carrier details, holder details, address, donor approval - Adds validation - Adds reusable input components to add customization to in the next step
- Loading branch information
Showing
25 changed files
with
2,491 additions
and
186 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
type Props = { | ||
children: React.ReactNode | ||
} | ||
|
||
export function EmbedField({ children }: Props) { | ||
// TODO: customizable classNames | ||
return ( | ||
<div className="GigsEmbeds GigsPortingEmbed GigsEmbeds-field"> | ||
{children} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
type Props = { | ||
error: string | ||
} | ||
|
||
export function EmbedFieldError({ error }: Props) { | ||
if (!error) { | ||
return null | ||
} | ||
|
||
// TODO: customizable classNames | ||
return ( | ||
<div className="GigsEmbeds GigsPortingEmbed GigsEmbeds-fieldError"> | ||
{error} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
type Props = React.HTMLAttributes<HTMLInputElement> | ||
|
||
export function EmbedFieldInput(props: Props) { | ||
// TODO: customizable classNames | ||
return ( | ||
<input | ||
className="GigsEmbeds GigsPortingEmbed GigsEmbeds-fieldInput" | ||
{...props} | ||
/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
type Props = React.HTMLAttributes<HTMLLabelElement> | ||
|
||
export function EmbedFieldLabel(props: Props) { | ||
// TODO: customizable classNames | ||
return ( | ||
<label | ||
className="GigsEmbeds GigsPortingEmbed GigsEmbeds-fieldLabel" | ||
{...props} | ||
/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,112 +1,73 @@ | ||
import { required, useForm } from '@modular-forms/preact' | ||
import { useSignalEffect } from '@preact/signals' | ||
|
||
import { Porting } from '../types' | ||
import { sanitizeSubmitData } from './sanitizeSubmitData' | ||
import { StepAddressForm, StepAddressFormData } from './StepAddressForm' | ||
import { | ||
StepCarrierDetailsForm, | ||
StepCarrierDetailsFormData, | ||
} from './StepCarrierDetailsForm' | ||
import { | ||
StepDonorProviderApprovalForm, | ||
StepDonorProviderApprovalFormData, | ||
} from './StepDonorProviderApprovalForm' | ||
import { | ||
StepHolderDetailsForm, | ||
StepHolderDetailsFormData, | ||
} from './StepHolderDetailsForm' | ||
import { wizardStep } from './wizardStep' | ||
|
||
type Props = { | ||
porting: Porting | ||
onValidationChange?: (event: { isValid: boolean }) => unknown | ||
onSubmit: (data: Partial<PortingForm>) => unknown | ||
onSubmit: (data: Partial<PortingFormData>) => unknown | ||
} | ||
|
||
type PortingForm = { | ||
accountPin: string | ||
accountNumber: string | ||
birthday: string | ||
firstName: string | ||
lastName: string | ||
} | ||
type PortingFormData = | ||
| StepCarrierDetailsFormData | ||
| StepHolderDetailsFormData | ||
| { address?: StepAddressFormData } | ||
| StepDonorProviderApprovalFormData | ||
|
||
export function PortingForm({ porting, onValidationChange, onSubmit }: Props) { | ||
const [portingForm, { Form, Field }] = useForm<PortingForm>({ | ||
initialValues: { | ||
accountNumber: porting.accountNumber || '', | ||
accountPin: '', | ||
birthday: porting.birthday || '', | ||
firstName: porting.firstName || '', | ||
lastName: porting.lastName || '', | ||
}, | ||
validateOn: 'blur', | ||
}) | ||
|
||
useSignalEffect(() => { | ||
const isValid = !portingForm.invalid.value | ||
onValidationChange?.({ isValid }) | ||
}) | ||
const step = wizardStep(porting) | ||
|
||
return ( | ||
<Form | ||
id="gigsPortingEmbedForm" // TODO: make customizable | ||
role="form" | ||
shouldDirty // only include changed fields in the onSubmit handler | ||
onSubmit={(data) => { | ||
const sanitizedData = sanitizeSubmitData(data) | ||
return onSubmit(sanitizedData) | ||
}} | ||
> | ||
<Field name="accountNumber" validate={[required('Please enter')]}> | ||
{(field, props) => ( | ||
<div> | ||
<label for="accountNumber">Account Number</label> | ||
<input | ||
id="accountNumber" | ||
type="text" | ||
value={field.value} | ||
{...props} | ||
/> | ||
{field.error && <div>{field.error}</div>} | ||
</div> | ||
)} | ||
</Field> | ||
if (step === 'carrierDetails') { | ||
return ( | ||
<StepCarrierDetailsForm | ||
porting={porting} | ||
onValidationChange={onValidationChange} | ||
onSubmit={(data) => onSubmit(data)} | ||
/> | ||
) | ||
} | ||
|
||
<Field | ||
name="accountPin" | ||
validate={porting.accountPinExists ? [] : [required('Please enter')]} | ||
> | ||
{(field, props) => ( | ||
<div> | ||
<label for="accountPin">Account PIN</label> | ||
<input | ||
id="accountPin" | ||
type="text" | ||
placeholder={porting.accountPinExists ? '••••' : undefined} | ||
{...props} | ||
/> | ||
{field.error && <div>{field.error}</div>} | ||
</div> | ||
)} | ||
</Field> | ||
if (step === 'holderDetails') { | ||
return ( | ||
<StepHolderDetailsForm | ||
porting={porting} | ||
onValidationChange={onValidationChange} | ||
onSubmit={(data) => onSubmit(data)} | ||
/> | ||
) | ||
} | ||
|
||
<Field name="birthday" validate={[required('Please enter')]}> | ||
{(field, props) => ( | ||
<div> | ||
<label for="birthday">Birthday</label> | ||
<input id="birthday" type="text" value={field.value} {...props} /> | ||
{field.error && <div>{field.error}</div>} | ||
</div> | ||
)} | ||
</Field> | ||
if (step === 'address') { | ||
return ( | ||
<StepAddressForm | ||
porting={porting} | ||
onValidationChange={onValidationChange} | ||
onSubmit={(data) => onSubmit({ address: data })} | ||
/> | ||
) | ||
} | ||
|
||
<Field name="firstName" validate={[required('Please enter')]}> | ||
{(field, props) => ( | ||
<div> | ||
<label for="firstName">First Name</label> | ||
<input id="firstName" type="text" value={field.value} {...props} /> | ||
{field.error && <div>{field.error}</div>} | ||
</div> | ||
)} | ||
</Field> | ||
if (step === 'donorApproval') { | ||
return ( | ||
<StepDonorProviderApprovalForm | ||
porting={porting} | ||
onValidationChange={onValidationChange} | ||
onSubmit={(data) => onSubmit(data)} | ||
/> | ||
) | ||
} | ||
|
||
<Field name="lastName" validate={[required('Please enter')]}> | ||
{(field, props) => ( | ||
<div> | ||
<label for="lastName">Last Name</label> | ||
<input id="lastName" type="text" value={field.value} {...props} /> | ||
{field.error && <div>{field.error}</div>} | ||
</div> | ||
)} | ||
</Field> | ||
</Form> | ||
) | ||
return null | ||
} |
Oops, something went wrong.