forked from reactplay/react-play
-
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.
Implement multistep form (reactplay#804)
* Update Readme as mistakely delete it * Create custom hoook for form handling * Create a form wrrapper * Create JSX CSS ffor form steps * Implementation LLogic for multistep form * Conver .jsx ext to .tsx for index file * Minor Fixes JSX aand CSS * Add cover image for play * Remove unused csss * Remove unused connsole logs * Typo fixes / Css fixes * Update Readme.md * Remove inline CSSS, Follow BEM convention * Final changes before pull request * Fix the formatting and linting errors * Revert changes done in others repository * Change the override selector. Add an unique class name * Show output in a Styled Div instead of alert * Include Header to play * Design Fix: Add padding and margin on small screen * Prefix CSS classes and Refactor Code Co-authored-by: Koustov <[email protected]> Co-authored-by: Tapas Adhikary <[email protected]>
- Loading branch information
1 parent
cb199a6
commit a2b7bc6
Showing
9 changed files
with
380 additions
and
0 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,14 @@ | ||
import React, { ReactNode } from 'react'; | ||
type FormWrapperProps = { | ||
title: string; | ||
children: ReactNode; | ||
}; | ||
|
||
export function FormWrapper({ title, children }: FormWrapperProps) { | ||
return ( | ||
<> | ||
<h2 className="form-wrapper-heading">{title}</h2> | ||
<div className="form-wrapper-content">{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,114 @@ | ||
import React from 'react'; | ||
import PlayHeader from 'common/playlists/PlayHeader'; | ||
import { useMultistepForm } from './customHook/useMultistepForm'; | ||
import PersonalInfo from './personalInfo'; | ||
import ContactInfo from './contactInfo'; | ||
import AccountInfo from './accountInfo'; | ||
import { FormEvent, useState } from 'react'; | ||
import './react-multistep-form.css'; | ||
type FormData = { | ||
firstName: string; | ||
lastName: string; | ||
address: string; | ||
email: string; | ||
password: string; | ||
}; | ||
const INITIAL_DATA: FormData = { | ||
firstName: '', | ||
lastName: '', | ||
address: '', | ||
email: '', | ||
password: '' | ||
}; | ||
|
||
function ReactMultistepForm(props: any) { | ||
const [data, setData] = useState(INITIAL_DATA); | ||
const [showReview, setShowReview] = useState(false); | ||
|
||
function updateFields(fields: Partial<FormData>) { | ||
setData((prev) => { | ||
return { ...prev, ...fields }; | ||
}); | ||
} | ||
const { isLastStep, isFirstStep, currentStepIndex, step, next, back, totalSteps } = | ||
useMultistepForm([ | ||
<PersonalInfo {...data} updateField={updateFields} />, | ||
<ContactInfo {...data} updateField={updateFields} />, | ||
<AccountInfo {...data} updateField={updateFields} /> | ||
]); | ||
|
||
const handleSubmit = (e: FormEvent) => { | ||
e.preventDefault(); | ||
if (!isLastStep) return next(); | ||
|
||
setShowReview(true); | ||
}; | ||
|
||
return ( | ||
<> | ||
<div className="play-details"> | ||
<PlayHeader play={props} /> | ||
<div className="play-details-body "> | ||
<div className="container multistep-play"> | ||
{!showReview && ( | ||
<form className="multistep-play-form" onSubmit={handleSubmit}> | ||
<div className="steps"> | ||
{currentStepIndex + 1}/{totalSteps} | ||
</div> | ||
{step} | ||
<div className="button-container"> | ||
{!isFirstStep && ( | ||
<button className="multistep-button" type="button" onClick={back}> | ||
Back | ||
</button> | ||
)} | ||
|
||
<button className="multistep-button" type="submit"> | ||
{isLastStep ? 'Finish' : 'Next'} | ||
</button> | ||
</div> | ||
</form> | ||
)} | ||
{showReview && ( | ||
<> | ||
<div className="review-container"> | ||
<h5 className="message-heading"> | ||
Thank you for submitting. Please review your details. | ||
</h5> | ||
<p className="user-info"> | ||
First Name: <span className="user-info-value">{data.firstName}</span> | ||
</p> | ||
<p className="user-info"> | ||
Last Name: <span className="user-info-value">{data.lastName}</span> | ||
</p> | ||
<p className="user-info"> | ||
Address: <span className="user-info-value">{data.address}</span> | ||
</p> | ||
<p className="user-info"> | ||
Email: <span className="user-info-value">{data.email}</span> | ||
</p> | ||
</div> | ||
<div className="button-container"> | ||
<button | ||
className="multistep-button" | ||
type="button" | ||
onClick={() => { | ||
setShowReview(false); | ||
}} | ||
> | ||
Back | ||
</button> | ||
</div> | ||
<div className="press-back-button-message"> | ||
<p>Please press Back button to edit your details.</p> | ||
</div> | ||
</> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
</> | ||
); | ||
} | ||
|
||
export default ReactMultistepForm; |
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,23 @@ | ||
# why-react | ||
|
||
A simple multistep form that can be used in any of your projects. | ||
|
||
## Play Demographic | ||
|
||
- Language: TS | ||
- Level: Beginner | ||
|
||
## Creator Information | ||
|
||
- User: abhishekh maharjan | ||
- Gihub Link: https://github.com/abhi886 | ||
- Blog: abhishekhmaharjan.com/blogs/react-multistep-form | ||
- Video: NA | ||
|
||
## Implementation Details | ||
|
||
Create a resusable hook that can convert any existing set of forms into a multistep form. | ||
|
||
## Resources | ||
|
||
NA |
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,48 @@ | ||
import React from 'react'; | ||
import { FormWrapper } from './FormWrapper'; | ||
type AccountData = { | ||
email: string; | ||
password: string; | ||
}; | ||
type AccountFormProps = AccountData & { | ||
updateField: (fields: Partial<AccountData>) => void; | ||
}; | ||
|
||
export default function accountInfo({ email, password, updateField }: AccountFormProps) { | ||
return ( | ||
<FormWrapper title="Account Information"> | ||
<label> | ||
{' '} | ||
Email address (*) | ||
<input | ||
autoFocus | ||
required | ||
className="form-input-text" | ||
pattern="[^ @]*@[^ @]*" | ||
placeholder="[email protected]" | ||
type="text" | ||
value={email} | ||
onChange={(e) => | ||
updateField({ | ||
email: e.target.value | ||
}) | ||
} | ||
/> | ||
</label> | ||
<label> | ||
Password (*) | ||
<input | ||
className="form-input-text" | ||
placeholder="Your password..." | ||
type="password" | ||
value={password} | ||
onChange={(e) => | ||
updateField({ | ||
password: e.target.value | ||
}) | ||
} | ||
/> | ||
</label> | ||
</FormWrapper> | ||
); | ||
} |
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,29 @@ | ||
import React from 'react'; | ||
import { FormWrapper } from './FormWrapper'; | ||
type ContactData = { | ||
address: string; | ||
}; | ||
type ContactFormProps = ContactData & { | ||
updateField: (fields: Partial<ContactData>) => void; | ||
}; | ||
|
||
export default function contactInfo({ address, updateField }: ContactFormProps) { | ||
return ( | ||
<FormWrapper title="Contact Information"> | ||
<label> | ||
Address (*) | ||
<input | ||
autoFocus | ||
required | ||
className="form-input-text" | ||
placeholder="Your address..." | ||
type="text" | ||
value={address} | ||
onChange={(e) => { | ||
updateField({ address: e.target.value }); | ||
}} | ||
/> | ||
</label> | ||
</FormWrapper> | ||
); | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions
37
src/plays/react-multistep-form/customHook/useMultistepForm.ts
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,37 @@ | ||
import { ReactElement } from 'react'; | ||
import { useState } from 'react'; | ||
|
||
export function useMultistepForm(steps: ReactElement[]) { | ||
const [currentStepIndex, setCurrentStepIndex] = useState(0); | ||
|
||
function next() { | ||
setCurrentStepIndex((i) => { | ||
if (i >= steps.length - 1) return i; | ||
|
||
return i + 1; | ||
}); | ||
} | ||
|
||
function back() { | ||
setCurrentStepIndex((i) => { | ||
if (i <= 0) return i; | ||
|
||
return i - 1; | ||
}); | ||
} | ||
|
||
function goTo(index: number) { | ||
setCurrentStepIndex(index); | ||
} | ||
|
||
return { | ||
currentStepIndex, | ||
step: steps[currentStepIndex], | ||
back, | ||
next, | ||
goTo, | ||
totalSteps: steps.length, | ||
isFirstStep: currentStepIndex === 0, | ||
isLastStep: currentStepIndex === steps.length - 1 | ||
}; | ||
} |
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,40 @@ | ||
import React from 'react'; | ||
import { FormWrapper } from './FormWrapper'; | ||
|
||
type PersonalData = { | ||
firstName: string; | ||
lastName: string; | ||
}; | ||
type PersonalFormProps = PersonalData & { | ||
updateField: (fields: Partial<PersonalData>) => void; | ||
}; | ||
|
||
export default function personalInfo({ firstName, lastName, updateField }: PersonalFormProps) { | ||
return ( | ||
<FormWrapper title="Personal Information"> | ||
<label> | ||
{' '} | ||
First name (*) | ||
<input | ||
autoFocus | ||
required | ||
className="form-input-text" | ||
placeholder="Your first name..." | ||
type="text" | ||
value={firstName} | ||
onChange={(e) => updateField({ firstName: e.target.value })} | ||
/> | ||
</label> | ||
<label> | ||
Last name | ||
<input | ||
className="form-input-text" | ||
placeholder="Your last name..." | ||
type="text" | ||
value={lastName} | ||
onChange={(e) => updateField({ lastName: e.target.value })} | ||
/> | ||
</label> | ||
</FormWrapper> | ||
); | ||
} |
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,75 @@ | ||
.multistep-play { | ||
border: 1px solid black; | ||
position: relative; | ||
background: white; | ||
padding: 1rem; | ||
border-radius: 0.5rem; | ||
font-family: Arial; | ||
max-width: 500px; | ||
margin: 0 auto; | ||
margin-top: 3%; | ||
} | ||
|
||
.multistep-play-form .steps { | ||
position: absolute; | ||
top: 2px; | ||
right: 0.5rem; | ||
} | ||
.button-container .multistep-button { | ||
border-radius: 8px; | ||
border: 1px solid; | ||
padding: 0.2em 0.5em; | ||
font-size: 1em; | ||
font-weight: 500; | ||
font-family: inherit; | ||
cursor: pointer; | ||
transition: border-color 0.25s; | ||
} | ||
.button-container .multistep-button:hover { | ||
border-color: #646cff; | ||
} | ||
.button-container .multistep-button:focus, | ||
.button-container .multistep-button:focus-visible { | ||
outline: 4px auto -webkit-focus-ring-color; | ||
} | ||
|
||
.multistep-play-form .form-input-text { | ||
width: 100%; | ||
padding: 10px 10px; | ||
margin: 8px 0; | ||
display: inline-block; | ||
border-radius: 4px; | ||
box-sizing: border-box; | ||
border: 1px solid black; | ||
font-size: 0.75rem; | ||
} | ||
|
||
.multistep-play .button-container { | ||
margin-top: 1rem; | ||
display: flex; | ||
gap: 0.5rem; | ||
justify-content: flex-end; | ||
} | ||
.multistep-play-form .form-wrapper-heading { | ||
text-align: center; | ||
margin: 0; | ||
padding-bottom: 1.5rem; | ||
font-weight: bold; | ||
} | ||
|
||
.multistep-play .review-container .message-heading { | ||
font-weight: bold; | ||
padding: 4px 0 4px 0; | ||
} | ||
.multistep-play .review-container .user-info { | ||
font-weight: 500; | ||
padding: 2px 0 2px 0; | ||
} | ||
.multistep-play .review-container .user-info .user-info-value { | ||
font-weight: normal; | ||
} | ||
|
||
.multistep-play .press-back-button-message { | ||
font-weight: 300; | ||
padding: 10px 0 5px 0; | ||
} |