@mui/material#Stepper TypeScript Examples

The following examples show how to use @mui/material#Stepper. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: form-stepper.tsx    From example with MIT License 5 votes vote down vote up
export function FormStepper({ steps, onComplete }: IFormStepperProps) {
	const [state, setFormResponse] = useReducer((prevState: any, action: {
		type: "next"
		response: any
	} | {
		type: "prev"
	}) => {
		const state = cloneDeep(prevState)
		switch (action.type) {
			case "next":
				state.responses[state.currentStep] = action.response
				state.currentStep = Math.min(state.currentStep + 1, steps.length - 1)
				return state
			case "prev":
				state.currentStep = Math.max(state.currentStep - 1, 0)
				return state
		}
	}, {
		currentStep: 0,
		responses: []
	})

	console.log(state)

	return (
		<>
			<Stepper activeStep={state.currentStep}>
				{
					steps.map((step, index) => {
						const last = index === steps.length - 1
						const lastCompleted = last && state.currentStep === steps.length - 1

						return <Step
							key={index}
							index={index}
							sx={{
								".Mui-completed": {
									color: lastCompleted ? "green" : "primary"
								}
							}}
							completed={lastCompleted ? true : undefined}
						>
							<StepLabel>{step.label}</StepLabel>
						</Step>
					})
				}
			</Stepper>
			<Box sx={{ mt: 4 }}>
				{steps[state.currentStep].render(
					(formResponse) => {
						setFormResponse({ type: "next", response: formResponse })
						if (state.currentStep === steps.length - 1) {
							onComplete?.(formResponse)
						}
					},
					state.responses[state.currentStep - 1]
				)}
			</Box>
			{
				state.currentStep > 0 &&
                <Box sx={{ mt: 2 }}>
                    <Button
                        startIcon={<Icon icon={faAngleLeft}/>}
                        color={"inherit"}
                        variant="text"
                        onClick={() => setFormResponse({ type: "prev" })}
                    >
                        Back
                    </Button>
                </Box>
			}
		</>
	)
}
Example #2
Source File: IrregularityForm.tsx    From frontend with MIT License 4 votes vote down vote up
export default function IrregularityForm({ campaign, person }: Props) {
  const { t } = useTranslation('irregularity')
  const classes = useStyles()

  const formRef = useRef<FormikProps<IrregularityFormData>>(null)

  const [fail, setFail] = useState(false)
  const [success, setSuccess] = useState(false)
  const [files, setFiles] = useState<File[]>([])
  const [activeStep, setActiveStep] = useState<Steps>(Steps.GREETING)
  const [failedStep, setFailedStep] = useState<Steps>(Steps.NONE)

  const steps: StepType[] = [
    {
      component: <Greeting />,
    },
    {
      component: <Contacts />,
    },
    {
      component: <Info files={files} setFiles={setFiles} />,
    },
  ]

  const isStepFailed = (step: Steps | number): boolean => {
    return step === failedStep
  }

  const mutation = useMutation<
    AxiosResponse<IrregularityResponse>,
    AxiosError<ApiErrors>,
    IrregularityInput
  >({
    mutationFn: createIrregularity,
  })

  const fileUploadMutation = useMutation<
    AxiosResponse<IrregularityUploadImage[]>,
    AxiosError<ApiErrors>,
    UploadIrregularityFiles
  >({
    mutationFn: uploadIrregularityFiles(),
  })

  const handleBack = () => {
    setActiveStep((prevActiveStep) => prevActiveStep - 1)
  }

  const handleSubmit = async (
    values: IrregularityFormData,
    actions: FormikHelpers<IrregularityFormData>,
  ) => {
    if (isLastStep(activeStep, steps)) {
      const errors = await actions.validateForm(values)
      const hasErrors = !!Object.keys(errors).length
      if (hasErrors) {
        setFailedStep(Steps.INFO)
        return
      }
      setActiveStep((prevActiveStep) => prevActiveStep + 1)
      setFailedStep(Steps.NONE)
      try {
        const data: IrregularityInput = {
          campaignId: values.info.campaignId,
          description: values.info.description,
          notifierType: values.info.notifierType,
          reason: values.info.reason,
          status: values.status,
          person: {
            firstName: values.person.firstName,
            lastName: values.person.lastName,
            email: values.person.email,
            phone: values.person.phone,
          },
        }
        const response = await mutation.mutateAsync(data)
        await fileUploadMutation.mutateAsync({
          files,
          irregularityId: response.data.id,
        })
        actions.resetForm()
        setSuccess(true)
      } catch (error) {
        console.error(error)
        setFail(true)
        if (isAxiosError(error)) {
          const { response } = error as AxiosError<ApiErrors>
          response?.data.message.map(({ property, constraints }) => {
            actions.setFieldError(property, t(matchValidator(constraints)))
          })
        }
      }

      return
    }

    actions.setTouched({})
    actions.setSubmitting(false)

    initialValues.info.campaignId = campaign.id
    initialValues.person.firstName = person?.firstName || ''
    initialValues.person.lastName = person?.lastName || ''
    initialValues.person.email = person?.email || ''
    initialValues.person.phone = person?.phone || ''

    await stepsHandler({ actions, activeStep, setActiveStep, setFailedStep })
  }

  if (success) {
    return <Success />
  }

  if (fail) {
    return <Fail setFail={setFail} setActiveStep={setActiveStep} />
  }

  return (
    <>
      <GenericForm<IrregularityFormData>
        onSubmit={handleSubmit}
        initialValues={initialValues}
        validationSchema={validationSchema[activeStep]}
        innerRef={formRef}>
        <Stepper
          alternativeLabel
          activeStep={activeStep}
          className={classes.stepper}
          connector={<ColorlibConnector />}>
          {steps.map((step, index) => (
            <Step key={index}>
              <StepLabel error={isStepFailed(index)} StepIconComponent={StepIcon} />
            </Step>
          ))}
        </Stepper>
        <div className={classes.content}>
          <Grid container spacing={5} justifyContent="center" className={classes.instructions}>
            <Grid container item xs={12}>
              {activeStep < steps.length && steps[activeStep].component}
            </Grid>
            <Grid container item spacing={3}>
              <Actions
                activeStep={activeStep}
                disableBack={activeStep === 0}
                onBack={handleBack}
                loading={mutation.isLoading}
                campaign={campaign}
                nextLabel={isLastStep(activeStep, steps) ? 'cta.submit' : 'cta.next'}
                backLabel={isFirstStep(activeStep, steps) ? 'cta.back-to-campaign' : 'cta.back'}
              />
            </Grid>
          </Grid>
        </div>
      </GenericForm>
      {activeStep === Steps.GREETING && <Remark text={t('steps.greeting.remark')} />}
      {activeStep === Steps.CONTACTS && <Remark text={t('steps.contacts.remark')} />}
    </>
  )
}
Example #3
Source File: FormikStepper.tsx    From frontend with MIT License 4 votes vote down vote up
export function FormikStepper({ children, ...props }: GenericFormProps<OneTimeDonation>) {
  const childrenArray = React.Children.toArray(children) as React.ReactElement<FormikStepProps>[]
  const { step, setStep } = useContext(StepsContext)
  const router = useRouter()
  useEffect(() => {
    router.query.success === 'false' || router.query.success === 'true' ? setStep(3) : null
  }, [router.query.success])
  const currentChild = childrenArray[step]

  const { data: currentPerson } = useCurrentPerson()
  function isLastStep() {
    return step === childrenArray.length - 2
  }

  function isFirstStep() {
    return step === 0
  }

  function isLogged() {
    if (currentPerson === undefined) {
      return false
    }
    if (currentPerson?.status && currentPerson.status !== 'unauthenticated') {
      return false
    }
    return true
  }
  const { t } = useTranslation('one-time-donation')

  return (
    <Formik
      {...props}
      validationSchema={currentChild.props.validationSchema}
      onSubmit={async (values, helpers) => {
        if (isLastStep()) {
          await props.onSubmit(values, helpers)
        } else if (isFirstStep() && isLogged()) {
          if (values.payment === 'bank') {
            router.push({
              pathname: router.route,
              query: {
                slug: router.query.slug,
                success: true,
              },
            })
          } else {
            setStep((s) => s + 2)
          }
        } else {
          setStep((s) => s + 1)
          helpers.setTouched({})
        }
      }}
      validateOnMount
      validateOnBlur>
      {({ isSubmitting, handleSubmit, isValid }) => (
        <Form
          onSubmit={handleSubmit}
          style={{
            maxWidth: '662px',
            marginLeft: 'auto',
            marginRight: 'auto',
          }}
          autoComplete="off">
          <StyledStepper>
            <Stepper alternativeLabel activeStep={step}>
              {childrenArray.map((child, index) => (
                <Step key={index}>
                  <StepLabel classes={{ alternativeLabel: classes.stepIcon }}>
                    {child.props.label}
                  </StepLabel>
                </Step>
              ))}
            </Stepper>
          </StyledStepper>
          <Box marginY={8}>{currentChild}</Box>
          {/* Controls of the form */}
          {step === 3 ? null : (
            <Grid container rowSpacing={2} columnSpacing={2}>
              <Grid item xs={12} md={6}>
                <Button
                  fullWidth
                  type="button"
                  variant="text"
                  disabled={step === 0 || isSubmitting}
                  color="error"
                  size="large"
                  onClick={() => {
                    if (step === 2 && isLogged()) {
                      setStep((s) => s - 2)
                      return
                    }
                    setStep((s) => s - 1)
                  }}>
                  {t('btns.back')}
                </Button>
              </Grid>
              <Grid item xs={12} md={6}>
                <LoadingButton
                  disabled={!isValid}
                  fullWidth
                  type="submit"
                  variant="contained"
                  loading={isSubmitting}
                  color="info"
                  size="large">
                  {isSubmitting ? 'Потвърждение' : isLastStep() ? t('btns.end') : t('btns.next')}
                </LoadingButton>
              </Grid>
            </Grid>
          )}
        </Form>
      )}
    </Formik>
  )
}
Example #4
Source File: SupportForm.tsx    From frontend with MIT License 4 votes vote down vote up
export default function SupportForm() {
  const { t } = useTranslation()

  const formRef = useRef<FormikProps<SupportFormData>>(null)
  const [activeStep, setActiveStep] = useState<Steps>(Steps.ROLES)
  const [failedStep, setFailedStep] = useState<Steps>(Steps.NONE)

  const mutation = useMutation<
    AxiosResponse<SupportRequestResponse>,
    AxiosError<ApiErrors>,
    SupportRequestInput
  >({
    mutationFn: createSupportRequest,
    onError: () => AlertStore.show(t('common:alerts.error'), 'error'),
    onSuccess: () => AlertStore.show(t('common:alerts.message-sent'), 'success'),
  })

  const handleBack = () => {
    setActiveStep((prevActiveStep) => prevActiveStep - 1)
  }

  const handleSubmit = async (values: SupportFormData, actions: FormikHelpers<SupportFormData>) => {
    if (isLastStep(activeStep, steps)) {
      const errors = await actions.validateForm()
      const hasErrors = !!Object.keys(errors).length
      if (hasErrors) {
        setFailedStep(Steps.PERSON)
        return
      }
      setActiveStep((prevActiveStep) => prevActiveStep + 1)
      setFailedStep(Steps.NONE)
      try {
        const { person, ...supportData } = values
        await mutation.mutateAsync({ person, supportData })
        actions.resetForm()
        if (window) {
          window.scrollTo({ top: 0, behavior: 'smooth' })
        }
      } catch (error) {
        console.error(error)
        if (isAxiosError(error)) {
          const { response } = error as AxiosError<ApiErrors>
          response?.data.message.map(({ property, constraints }) => {
            actions.setFieldError(property, t(matchValidator(constraints)))
          })
        }
      }

      return
    }

    actions.setTouched({})
    actions.setSubmitting(false)
    switch (activeStep) {
      case Steps.ROLES:
        {
          const errors = await actions.validateForm()
          if (errors.roles) {
            setFailedStep(Steps.ROLES)
            return
          }
          setActiveStep((prevActiveStep) => prevActiveStep + 1)
          setFailedStep(Steps.NONE)
        }
        break
      case Steps.QUESTIONS:
        {
          const errors = await actions.validateForm()
          let hasErrors = false
          const questions = Object.entries(values.roles)
            .filter(([, value]) => value)
            .map(([key]) => key)

          Object.keys(errors).forEach((error) => {
            if (questions.includes(error)) {
              hasErrors = true
            }
          })

          if (hasErrors) {
            setFailedStep(Steps.QUESTIONS)
            return
          }
          setActiveStep((prevActiveStep) => prevActiveStep + 1)
          setFailedStep(Steps.NONE)
        }
        break
      case Steps.PERSON:
        {
          const errors = await actions.validateForm()
          if (errors.person) {
            setFailedStep(Steps.PERSON)
            return
          }
          setActiveStep((prevActiveStep) => prevActiveStep + 1)
          setFailedStep(Steps.NONE)
        }
        break
      default:
        return 'Unknown step'
    }
  }

  const isStepFailed = (step: Steps | number): boolean => {
    return step === failedStep
  }

  const isLastStep = (activeStep: number, steps: StepType[]): boolean => {
    return activeStep === steps.length - 2
  }

  const isThankYouStep = (activeStep: number, steps: StepType[]): boolean => {
    return activeStep === steps.length - 1
  }

  return (
    <GenericForm<SupportFormData>
      onSubmit={handleSubmit}
      initialValues={initialValues}
      validationSchema={validationSchema[activeStep]}
      innerRef={formRef}>
      <Hidden mdDown>
        <Stepper
          alternativeLabel
          activeStep={activeStep}
          connector={<StepConnector sx={{ mt: 1.5 }} />}>
          {steps.map((step, index) => (
            <Step key={index}>
              <StepLabel error={isStepFailed(index)} StepIconComponent={StepIcon}>
                {t(step.label)}
              </StepLabel>
            </Step>
          ))}
        </Stepper>
      </Hidden>
      {isThankYouStep(activeStep, steps) ? (
        steps[activeStep].component
      ) : (
        <Box sx={{ display: 'flex', justifyContent: 'center' }}>
          <Grid container justifyContent="center">
            <Grid item xs={12} sx={{ mt: 1, mb: 5 }}>
              {steps[activeStep].component}
            </Grid>
            <Grid item xs={12} sx={(theme) => ({ '& button': { minWidth: theme.spacing(12) } })}>
              <Actions
                disableBack={activeStep === 0}
                onBack={handleBack}
                loading={mutation.isLoading}
                nextLabel={
                  isLastStep(activeStep, steps) ? 'support:cta.submit' : 'support:cta.next'
                }
              />
            </Grid>
          </Grid>
        </Box>
      )}
    </GenericForm>
  )
}