Skip to content

Commit

Permalink
Add forms for wizard steps
Browse files Browse the repository at this point in the history
  • Loading branch information
timomeh committed Feb 27, 2024
1 parent 5abc401 commit c1cac07
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 97 deletions.
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 {
StepDonorApprovalForm,
StepDonorApprovalFormData,
} from './StepDonorApproval'
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 }
| StepDonorApprovalFormData

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 (
<StepDonorApprovalForm
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
}
62 changes: 62 additions & 0 deletions lib/PortingEmbed/StepAddressForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useForm } from '@modular-forms/preact'
import { useSignalEffect } from '@preact/signals'

import { Porting } from '../types'

export type StepAddressFormData = {
city: string
country: string
line1: string
line2: string | null
postalCode: string
state: string | null
}

type Props = {
porting: Porting
onValidationChange?: (event: { isValid: boolean }) => unknown
onSubmit: (data: StepAddressFormData) => unknown
}

export function StepAddressForm({
porting,
onValidationChange,
onSubmit,
}: Props) {
const [portingForm, { Form, Field }] = useForm<StepAddressFormData>({

Check failure on line 26 in lib/PortingEmbed/StepAddressForm.tsx

View workflow job for this annotation

GitHub Actions / build

'Field' is declared but its value is never read.

Check failure on line 26 in lib/PortingEmbed/StepAddressForm.tsx

View workflow job for this annotation

GitHub Actions / lint

'Field' is assigned a value but never used. Allowed unused vars must match /^_/u
initialValues: {
city: porting.address?.city ?? '',
country: porting.address?.country ?? '',
line1: porting.address?.line1 ?? '',
line2: porting.address?.line2 ?? '',
postalCode: porting.address?.postalCode ?? '',
state: porting.address?.state ?? '',
},
validateOn: 'blur',
})

useSignalEffect(() => {
const isValid = !portingForm.invalid.value
onValidationChange?.({ isValid })
})

return (
<Form
id="gigsPortingEmbedForm" // TODO: make customizable
role="form"
onSubmit={(data) => {
// The address form is always submitted as a whole, never partially.
// line2 and state are optional and must be converted to null if empty.
const sanitizedData = {
...data,
line2: data.line2 || null,
state: data.state || null,
}

return onSubmit(sanitizedData)
}}
>
{porting.id}
</Form>
)
}
49 changes: 49 additions & 0 deletions lib/PortingEmbed/StepCarrierDetailsForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useForm } from '@modular-forms/preact'
import { useSignalEffect } from '@preact/signals'

import { Porting } from '../types'
import { sanitizeSubmitData } from './sanitizeSubmitData'

export type StepCarrierDetailsFormData = {
accountNumber?: string
accountPin?: string
}

type Props = {
porting: Porting
onValidationChange?: (event: { isValid: boolean }) => unknown
onSubmit: (data: Partial<StepCarrierDetailsFormData>) => unknown
}

export function StepCarrierDetailsForm({
porting,
onValidationChange,
onSubmit,
}: Props) {
const [portingForm, { Form, Field }] = useForm<StepCarrierDetailsFormData>({

Check failure on line 23 in lib/PortingEmbed/StepCarrierDetailsForm.tsx

View workflow job for this annotation

GitHub Actions / build

'Field' is declared but its value is never read.

Check failure on line 23 in lib/PortingEmbed/StepCarrierDetailsForm.tsx

View workflow job for this annotation

GitHub Actions / lint

'Field' is assigned a value but never used. Allowed unused vars must match /^_/u
initialValues: {
accountNumber: porting.accountNumber ?? '',
accountPin: '',
},
validateOn: 'blur',
})

useSignalEffect(() => {
const isValid = !portingForm.invalid.value
onValidationChange?.({ isValid })
})

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)
}}
>
{porting.id}
</Form>
)
}
47 changes: 47 additions & 0 deletions lib/PortingEmbed/StepDonorApproval.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useForm } from '@modular-forms/preact'
import { useSignalEffect } from '@preact/signals'

import { Porting } from '../types'
import { sanitizeSubmitData } from './sanitizeSubmitData'

export type StepDonorApprovalFormData = {
donorProviderApproval?: boolean
}

type Props = {
porting: Porting
onValidationChange?: (event: { isValid: boolean }) => unknown
onSubmit: (data: Partial<StepDonorApprovalFormData>) => unknown
}

export function StepDonorApprovalForm({
porting,
onValidationChange,
onSubmit,
}: Props) {
const [portingForm, { Form, Field }] = useForm<StepDonorApprovalFormData>({

Check failure on line 22 in lib/PortingEmbed/StepDonorApproval.tsx

View workflow job for this annotation

GitHub Actions / build

'Field' is declared but its value is never read.

Check failure on line 22 in lib/PortingEmbed/StepDonorApproval.tsx

View workflow job for this annotation

GitHub Actions / lint

'Field' is assigned a value but never used. Allowed unused vars must match /^_/u
initialValues: {
donorProviderApproval: porting.donorProviderApproval ?? false,
},
validateOn: 'blur',
})

useSignalEffect(() => {
const isValid = !portingForm.invalid.value
onValidationChange?.({ isValid })
})

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)
}}
>
{porting.id}
</Form>
)
}
51 changes: 51 additions & 0 deletions lib/PortingEmbed/StepHolderDetailsForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useForm } from '@modular-forms/preact'
import { useSignalEffect } from '@preact/signals'

import { Porting } from '../types'
import { sanitizeSubmitData } from './sanitizeSubmitData'

export type StepHolderDetailsFormData = {
firstName?: string
lastName?: string
birthday?: string
}

type Props = {
porting: Porting
onValidationChange?: (event: { isValid: boolean }) => unknown
onSubmit: (data: Partial<StepHolderDetailsFormData>) => unknown
}

export function StepHolderDetailsForm({
porting,
onValidationChange,
onSubmit,
}: Props) {
const [portingForm, { Form, Field }] = useForm<StepHolderDetailsFormData>({

Check failure on line 24 in lib/PortingEmbed/StepHolderDetailsForm.tsx

View workflow job for this annotation

GitHub Actions / build

'Field' is declared but its value is never read.

Check failure on line 24 in lib/PortingEmbed/StepHolderDetailsForm.tsx

View workflow job for this annotation

GitHub Actions / lint

'Field' is assigned a value but never used. Allowed unused vars must match /^_/u
initialValues: {
firstName: porting.firstName ?? '',
lastName: porting.lastName ?? '',
birthday: porting.birthday ?? '',
},
validateOn: 'blur',
})

useSignalEffect(() => {
const isValid = !portingForm.invalid.value
onValidationChange?.({ isValid })
})

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)
}}
>
{porting.id}
</Form>
)
}

0 comments on commit c1cac07

Please sign in to comment.