Skip to content

ianwilk20/multi-step-form

Repository files navigation

Frontend Mentor - Multi-step form solution

This is a solution to the Multi-step form challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.

Table of contents

Overview

The challenge

Users should be able to:

  • Complete each step of the sequence
  • Go back to a previous step to update their selections
  • See a summary of their selections on the final step and confirm their order
  • View the optimal layout for the interface depending on their device's screen size
  • See hover and focus states for all interactive elements on the page
  • Receive form validation messages if:
    • A field has been missed
    • The email address is not formatted correctly
    • A step is submitted, but no selection has been made

Screenshot

Mobile:

Mobile step 1 Mobile step 2 Mobile step 3 Mobile step 4 Mobile completed form

Desktop:

Desktop step 1 Desktop step 2 Desktop step 3 Desktop step 4 Desktop completed form

Links

My process

Built with

  • Semantic HTML5 markup
  • CSS custom properties
  • Flexbox
  • Mobile-first workflow
  • Tailwind - For styles
  • React - JS library
  • Formik - Form library

What I learned

  • I learned the basics of the Formik library to develop this multi-step form.
  • I ran into a bug where the form values setState wasn't working as expected. When a form was submitted it would first call updateFormValues() then updateFormStep(). Here was my original code:
const { formValues, setFormValues } = useContext(FormContext)
const updateFormValues = (newValues) => {
    setFormValues({ ...formValues, ...newValues })
}

const updateFormStep = (newStep) => {
  setFormValues({ ...prevState, step: newStep })
}

It turns out that updateFormStep()'s setFormValues was not getting the most up-to-date formValues because React has a tendency of batching state updates togther. As a result, I modified the updateFormStep() function as follows:

const updateFormStep = (newStep) => {
  setFormValues((prevState) => ({ ...prevState, step: newStep }))
}

Now the setFormValues will always provide us with the latest state (prevState) and we use that in updating our state. Meaning, our state management is more robust and predicatable.

  • I learned how to make the the navigation bar always stays at the bottom without overlapping the expanding form. It involved using a flexbox layout to ensure that the navigation bar always stays at the bottom of the continaer while allowing the form to expand as needed. To accomplish this my flexbox container that would hold my form and navigation bar has a height of 100vh, sits above all of the page content, and has a margin to offset it from the top of the page (as shown in the mockup). The form inside the flexbox container has a flex-grow: 1 will occupy all the available vertical space in the container, but it won't encroach upon or overlap the navigation bar. If the form needs to grow beyond the container's (like when the form expands due to hidden fields) scrolling will occur within the container. The navigation bar sits at the bottom of the container always.

  • I learned that image elements behave differently compared to other elements. The image displayed in the background of the navigation step tracker was what I was having trouble with. I had specified a fixed width and height for the container that was holding the image and the image was overflowing but what I wanted was for the image to take up the space max height it could within the container. I was not setting a fixed height and width for the image, rather I was setting height: 100%, and because images have intrinsic dimensions the image was maintaining its original size ratio which meant it was overflowing. Perhaps limiting its width would have been another approach to solve this issue, but it may have meant magic numbering things at different screen sizes to ensure responsivity. What I did instead was learn to constrain the image by using object-fit: cover so that the image would not overflow and would properly scale within the container.

Continued development

I think there could have been an oppourtunity for a higher level component that would let you provide fields you want on a given form step as component parameters, and the field elements as children to the component. That would have probably cut back on a lot of repeated code across steps 1-4 of the form and made it simpler going forward should someone need to add new fields or get rid of existing ones.

In another vein, I'd like to start adding TypeScript to my projects as that is something I've been meaning to learn to make my apps type safe.

Useful resources

  • Formik documentation - This helped me learn the basics of Formik to be able to implement this form.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors