Skip to content

Commit

Permalink
Split the Porting Embed Form into a Wizard (#4)
Browse files Browse the repository at this point in the history
- 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
timomeh authored Mar 1, 2024
1 parent a4c02e5 commit 96befd7
Show file tree
Hide file tree
Showing 25 changed files with 2,491 additions and 186 deletions.
12 changes: 12 additions & 0 deletions lib/PortingEmbed/EmbedField.tsx
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>
)
}
16 changes: 16 additions & 0 deletions lib/PortingEmbed/EmbedFieldError.tsx
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>
)
}
11 changes: 11 additions & 0 deletions lib/PortingEmbed/EmbedFieldInput.tsx
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}
/>
)
}
11 changes: 11 additions & 0 deletions lib/PortingEmbed/EmbedFieldLabel.tsx
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}
/>
)
}
2 changes: 1 addition & 1 deletion lib/PortingEmbed/PortingEmbed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function PortingEmbed({
onValidationChange,
}: PortingEmbedProps) {
return (
<div className="__gigsPortingEmbed">
<div className="__ge_portingRoot">
<PortingForm
porting={porting}
onValidationChange={onValidationChange}
Expand Down
155 changes: 58 additions & 97 deletions lib/PortingEmbed/PortingForm.tsx
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
}
Loading

0 comments on commit 96befd7

Please sign in to comment.