Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"rules": {
"prettier/prettier": [
"error",
1,
{
"singleQuote": true
}
Expand All @@ -23,4 +23,4 @@
}
]
}
}
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
This project refers to the article https://medium.com/@nphivu414/build-a-multi-step-form-with-react-hooks-formik-yup-and-materialui-fa4f73545598

This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

## Available Scripts
Expand Down
26 changes: 15 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"name": "app",
"name": "building-multi-step-form-with-formik-and-yup",
"version": "0.1.0",
"private": true,
"dependencies": {
"@date-io/date-fns": "^1.3.13",
"@material-ui/core": "^4.9.0",
"@material-ui/pickers": "^3.2.10",
"@emotion/react": "11.7.1",
"@emotion/styled": "11.6.0",
"@mui/lab": "5.0.0-alpha.67",
"@mui/material": "5.4.0",
"@testing-library/jest-dom": "^5.0.2",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^8.1.0",
Expand All @@ -14,9 +16,9 @@
"lodash": "^4.17.15",
"moment": "^2.24.0",
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-scripts": "3.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^5.0.0",
"yup": "^0.28.1"
},
"scripts": {
Expand All @@ -41,9 +43,11 @@
]
},
"devDependencies": {
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.18.0",
"eslint-plugin-react-hooks": "^2.3.0",
"prettier": "1.19.1"
}
}
"eslint-plugin-react-hooks": "^4.3.0",
"prettier": "2.5.1"
},
"keywords": [],
"description": ""
}
31 changes: 11 additions & 20 deletions src/components/CheckoutPage/CheckoutPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
StepLabel,
Button,
Typography,
CircularProgress
} from '@material-ui/core';
CircularProgress,
} from '@mui/material';
import { Formik, Form } from 'formik';

import AddressForm from './Forms/AddressForm';
Expand All @@ -18,7 +18,7 @@ import validationSchema from './FormModel/validationSchema';
import checkoutFormModel from './FormModel/checkoutFormModel';
import formInitialValues from './FormModel/formInitialValues';

import useStyles from './styles';
//import useStyles from "./styles";

const steps = ['Shipping address', 'Payment details', 'Review your order'];
const { formId, formField } = checkoutFormModel;
Expand All @@ -37,13 +37,12 @@ function _renderStepContent(step) {
}

export default function CheckoutPage() {
const classes = useStyles();
const [activeStep, setActiveStep] = useState(0);
const currentValidationSchema = validationSchema[activeStep];
const isLastStep = activeStep === steps.length - 1;

function _sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
return new Promise((resolve) => setTimeout(resolve, ms));
}

async function _submitForm(values, actions) {
Expand Down Expand Up @@ -73,8 +72,8 @@ export default function CheckoutPage() {
<Typography component="h1" variant="h4" align="center">
Checkout
</Typography>
<Stepper activeStep={activeStep} className={classes.stepper}>
{steps.map(label => (
<Stepper activeStep={activeStep}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
Expand All @@ -93,28 +92,20 @@ export default function CheckoutPage() {
<Form id={formId}>
{_renderStepContent(activeStep)}

<div className={classes.buttons}>
<div>
{activeStep !== 0 && (
<Button onClick={_handleBack} className={classes.button}>
Back
</Button>
<Button onClick={_handleBack}>Back</Button>
)}
<div className={classes.wrapper}>
<div>
<Button
disabled={isSubmitting}
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
{isLastStep ? 'Place order' : 'Next'}
{isLastStep ? 'Place order' : 'Next'}
</Button>
{isSubmitting && (
<CircularProgress
size={24}
className={classes.buttonProgress}
/>
)}
{isSubmitting && <CircularProgress size={24} />}
</div>
</div>
</Form>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Typography } from '@material-ui/core';
import { Typography } from '@mui/material';

function CheckoutSuccess() {
return (
Expand Down
34 changes: 18 additions & 16 deletions src/components/CheckoutPage/FormModel/checkoutFormModel.js
Original file line number Diff line number Diff line change
@@ -1,71 +1,73 @@
export default {
const checkoutFormModel = {
formId: 'checkoutForm',
formField: {
firstName: {
name: 'firstName',
label: 'First name*',
requiredErrorMsg: 'First name is required'
requiredErrorMsg: 'First name is required',
},
lastName: {
name: 'lastName',
label: 'Last name*',
requiredErrorMsg: 'Last name is required'
requiredErrorMsg: 'Last name is required',
},
address1: {
name: 'address1',
label: 'Address Line 1*',
requiredErrorMsg: 'Address Line 1 is required'
requiredErrorMsg: 'Address Line 1 is required',
},
address2: {
name: 'address2',
label: 'Address Line 2'
label: 'Address Line 2',
},
city: {
name: 'city',
label: 'City*',
requiredErrorMsg: 'City is required'
requiredErrorMsg: 'City is required',
},
state: {
name: 'state',
label: 'State/Province/Region'
label: 'State/Province/Region',
},
zipcode: {
name: 'zipcode',
label: 'Zipcode*',
requiredErrorMsg: 'Zipcode is required',
invalidErrorMsg: 'Zipcode is not valid (e.g. 70000)'
invalidErrorMsg: 'Zipcode is not valid (e.g. 70000)',
},
country: {
name: 'country',
label: 'Country*',
requiredErrorMsg: 'Country is required'
requiredErrorMsg: 'Country is required',
},
useAddressForPaymentDetails: {
name: 'useAddressForPaymentDetails',
label: 'Use this address for payment details'
label: 'Use this address for payment details',
},
nameOnCard: {
name: 'nameOnCard',
label: 'Name on card*',
requiredErrorMsg: 'Name on card is required'
requiredErrorMsg: 'Name on card is required',
},
cardNumber: {
name: 'cardNumber',
label: 'Card number*',
requiredErrorMsg: 'Card number is required',
invalidErrorMsg: 'Card number is not valid (e.g. 4111111111111)'
invalidErrorMsg: 'Card number is not valid (e.g. 4111111111111)',
},
expiryDate: {
name: 'expiryDate',
label: 'Expiry date*',
requiredErrorMsg: 'Expiry date is required',
invalidErrorMsg: 'Expiry date is not valid'
invalidErrorMsg: 'Expiry date is not valid',
},
cvv: {
name: 'cvv',
label: 'CVV*',
requiredErrorMsg: 'CVV is required',
invalidErrorMsg: 'CVV is invalid (e.g. 357)'
}
}
invalidErrorMsg: 'CVV is invalid (e.g. 357)',
},
},
};

export default checkoutFormModel;
6 changes: 3 additions & 3 deletions src/components/CheckoutPage/FormModel/formInitialValues.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const {
nameOnCard,
cardNumber,
expiryDate,
cvv
}
cvv,
},
} = checkoutFormModel;

export default {
Expand All @@ -26,5 +26,5 @@ export default {
[nameOnCard.name]: '',
[cardNumber.name]: '',
[expiryDate.name]: '',
[cvv.name]: ''
[cvv.name]: '',
};
18 changes: 8 additions & 10 deletions src/components/CheckoutPage/FormModel/validationSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const {
nameOnCard,
cardNumber,
expiryDate,
cvv
}
cvv,
},
} = checkoutFormModel;

const visaRegEx = /^(?:4[0-9]{12}(?:[0-9]{3})?)$/;
Expand All @@ -23,19 +23,17 @@ export default [
[firstName.name]: Yup.string().required(`${firstName.requiredErrorMsg}`),
[lastName.name]: Yup.string().required(`${lastName.requiredErrorMsg}`),
[address1.name]: Yup.string().required(`${address1.requiredErrorMsg}`),
[city.name]: Yup.string()
.nullable()
.required(`${city.requiredErrorMsg}`),
[city.name]: Yup.string().nullable().required(`${city.requiredErrorMsg}`),
[zipcode.name]: Yup.string()
.required(`${zipcode.requiredErrorMsg}`)
.test(
'len',
`${zipcode.invalidErrorMsg}`,
val => val && val.length === 5
(val) => val && val.length === 5
),
[country.name]: Yup.string()
.nullable()
.required(`${country.requiredErrorMsg}`)
.required(`${country.requiredErrorMsg}`),
}),
Yup.object().shape({
[nameOnCard.name]: Yup.string().required(`${nameOnCard.requiredErrorMsg}`),
Expand All @@ -45,7 +43,7 @@ export default [
[expiryDate.name]: Yup.string()
.nullable()
.required(`${expiryDate.requiredErrorMsg}`)
.test('expDate', expiryDate.invalidErrorMsg, val => {
.test('expDate', expiryDate.invalidErrorMsg, (val) => {
if (val) {
const startDate = new Date();
const endDate = new Date(2050, 12, 31);
Expand All @@ -58,6 +56,6 @@ export default [
}),
[cvv.name]: Yup.string()
.required(`${cvv.requiredErrorMsg}`)
.test('len', `${cvv.invalidErrorMsg}`, val => val && val.length === 3)
})
.test('len', `${cvv.invalidErrorMsg}`, (val) => val && val.length === 3),
}),
];
Loading