Skip to content

Commit

Permalink
Implement multistep form (reactplay#804)
Browse files Browse the repository at this point in the history
* 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
3 people committed Jan 20, 2023
1 parent cb199a6 commit a2b7bc6
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/plays/react-multistep-form/FormWrapper.tsx
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>
</>
);
}
114 changes: 114 additions & 0 deletions src/plays/react-multistep-form/ReactMultistepForm.tsx
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;
23 changes: 23 additions & 0 deletions src/plays/react-multistep-form/Readme.md
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
48 changes: 48 additions & 0 deletions src/plays/react-multistep-form/accountInfo.tsx
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>
);
}
29 changes: 29 additions & 0 deletions src/plays/react-multistep-form/contactInfo.tsx
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>
);
}
Binary file added src/plays/react-multistep-form/cover.png
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 src/plays/react-multistep-form/customHook/useMultistepForm.ts
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
};
}
40 changes: 40 additions & 0 deletions src/plays/react-multistep-form/personalInfo.tsx
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>
);
}
75 changes: 75 additions & 0 deletions src/plays/react-multistep-form/react-multistep-form.css
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;
}

0 comments on commit a2b7bc6

Please sign in to comment.