@material-ui/core#StepButton TypeScript Examples

The following examples show how to use @material-ui/core#StepButton. 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: Stepper.tsx    From storefront with MIT License 5 votes vote down vote up
Stepper: React.FC<StepperProps> = ({
  activeStep: initialActiveStep,
  children,
  onChangeStep,
  steps,
  ...props
}) => {
  const styles = useStyles();
  const [activeStep, setActiveStep] = useState(
    steps.findIndex((step) => step.path === initialActiveStep),
  );

  useEffect(() => {
    if (onChangeStep != null) {
      onChangeStep(steps[activeStep]);
    }
  }, [activeStep, onChangeStep, steps]);

  const handleStep = (step: number) => () => {
    setActiveStep(step);
  };

  const handleNext = () => {
    setActiveStep((prevActiveStep) => prevActiveStep + 1);
  };

  const handlePrevious =
    (step = activeStep - 1) =>
    () => {
      if (step < activeStep && step >= 0) {
        setActiveStep(step);
      }
    };

  return (
    <>
      <MuiStepper
        alternativeLabel
        activeStep={activeStep}
        className={styles.root}
        orientation="horizontal"
        {...props}
      >
        {steps.map((step, i) => (
          <Step key={step.path}>
            <StepButton onClick={handleStep(i)}>{step.title}</StepButton>
          </Step>
        ))}
      </MuiStepper>
      <Divider />
      {children({ handleNext, handlePrevious })[activeStep]}
    </>
  );
}
Example #2
Source File: TaskPage.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
TaskStatusStepper = memo(
  (props: {
    steps: TaskStep[];
    currentStepId: string | undefined;
    onUserStepChange: (id: string) => void;
    classes?: {
      root?: string;
    };
  }) => {
    const { steps, currentStepId, onUserStepChange } = props;
    const classes = useStyles(props);

    return (
      <div className={classes.root}>
        <Stepper
          activeStep={steps.findIndex(s => s.id === currentStepId)}
          orientation="vertical"
          nonLinear
        >
          {steps.map((step, index) => {
            const isCompleted = step.status === 'completed';
            const isFailed = step.status === 'failed';
            const isActive = step.status === 'processing';
            const isSkipped = step.status === 'skipped';

            return (
              <Step key={String(index)} expanded>
                <StepButton onClick={() => onUserStepChange(step.id)}>
                  <StepLabel
                    StepIconProps={{
                      completed: isCompleted,
                      error: isFailed,
                      active: isActive,
                    }}
                    StepIconComponent={TaskStepIconComponent}
                    className={classes.stepWrapper}
                  >
                    <div className={classes.labelWrapper}>
                      <Typography variant="subtitle2">{step.name}</Typography>
                      {isSkipped ? (
                        <Typography variant="caption">Skipped</Typography>
                      ) : (
                        <StepTimeTicker step={step} />
                      )}
                    </div>
                  </StepLabel>
                </StepButton>
              </Step>
            );
          })}
        </Stepper>
      </div>
    );
  },
)
Example #3
Source File: Wizard.tsx    From twilio-voice-notification-app with Apache License 2.0 5 votes vote down vote up
Wizard = ({ data, activeStep }: StepProps) => {
  // Info about completition status of each step.
  const stepsCompletion = useSelector(stepsCompletionSelector);

  return (
    <Box>
      <Stepper
        activeStep={activeStep && routerSteps.indexOf(activeStep)}
        alternativeLabel
        connector={<CustomConnector />}
        style={{ backgroundColor: 'transparent' }}
      >
        {routerSteps.map((step) => {
          const { label } = step;
          const completed =
            !isStepAhead(activeStep, step) && stepsCompletion[step.key];

          return (
            <Step key={label} disabled={true}>
              <StepButton
                icon={
                  <WizardStepIcon
                    icon={step.icon}
                    active={activeStep === step}
                    completed={completed}
                  />
                }
              >
                <StyledStepLabel>{label}</StyledStepLabel>
              </StepButton>
            </Step>
          );
        })}
      </Stepper>
      <Switch>
        {routerSteps.map((step) => (
          <Route
            key={step.key}
            path={step.location}
            render={() => React.createElement(step.component, { step, data })}
          />
        ))}
        <Redirect from="*" to="/create/configure" />
      </Switch>
    </Box>
  );
}
Example #4
Source File: ExperimentForm.tsx    From abacus with GNU General Public License v2.0 4 votes vote down vote up
ExperimentForm = ({
  indexedMetrics,
  indexedSegments,
  initialExperiment,
  onSubmit,
  completionBag,
  formSubmissionError,
}: {
  indexedMetrics: Record<number, Metric>
  indexedSegments: Record<number, Segment>
  initialExperiment: ExperimentFormData
  completionBag: ExperimentFormCompletionBag
  onSubmit: (formData: unknown) => Promise<void>
  formSubmissionError?: Error
}): JSX.Element => {
  const classes = useStyles()

  const rootRef = useRef<HTMLDivElement>(null)

  const [currentStageId, setActiveStageId] = useState<StageId>(StageId.Beginning)
  const currentStageIndex = stages.findIndex((stage) => stage.id === currentStageId)

  const [completeStages, setCompleteStages] = useState<StageId[]>([])
  const [errorStages, setErrorStages] = useState<StageId[]>([])

  useEffect(() => {
    rootRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'start' })
  }, [currentStageId])

  // Preventing accidental non-react-router navigate-aways:
  const preventSubmissionRef = useRef<boolean>(false)
  useEffect(() => {
    // istanbul ignore next; trivial
    // Sure we can test that these lines run but what is really important is how they
    // behave in browsers, which IMO is too complicated to write tests for in this case.
    const eventListener = (event: BeforeUnloadEvent) => {
      if (preventSubmissionRef.current) {
        event.preventDefault()
        // Chrome requires returnValue to be set
        event.returnValue = ''
      }
    }
    window.addEventListener('beforeunload', eventListener)
    return () => {
      window.removeEventListener('beforeunload', eventListener)
    }
  }, [])

  return (
    <Formik
      initialValues={{ experiment: initialExperiment }}
      onSubmit={onSubmit}
      validationSchema={yup.object({ experiment: experimentFullNewSchema })}
    >
      {(formikProps) => {
        const getStageErrors = async (stage: Stage) => {
          return _.pick(await formikProps.validateForm(), stage.validatableFields)
        }

        const isStageValid = async (stage: Stage): Promise<boolean> => {
          const errors = await formikProps.validateForm()
          return !stage.validatableFields.some((field) => _.get(errors, field))
        }

        const updateStageState = async (stage: Stage) => {
          if (stage.id === StageId.Submit) {
            return
          }

          if (await isStageValid(stage)) {
            setErrorStages((prevValue) => _.difference(prevValue, [stage.id]))
            setCompleteStages((prevValue) => _.union(prevValue, [stage.id]))
          } else {
            setErrorStages((prevValue) => _.union(prevValue, [stage.id]))
            setCompleteStages((prevValue) => _.difference(prevValue, [stage.id]))
          }
        }

        const changeStage = (stageId: StageId) => {
          setActiveStageId(stageId)
          void updateStageState(stages[currentStageIndex])

          if (errorStages.includes(stageId)) {
            void getStageErrors(stages[stageId]).then((stageErrors) =>
              formikProps.setTouched(setNestedObjectValues(stageErrors, true)),
            )
          }
          if (stageId === StageId.Submit) {
            stages.map(updateStageState)
          }
        }

        const prevStage = () => {
          const prevStage = stages[currentStageIndex - 1]
          prevStage && changeStage(prevStage.id)
        }
        const nextStage = () => {
          const nextStage = stages[currentStageIndex + 1]
          nextStage && changeStage(nextStage.id)
        }

        preventSubmissionRef.current = formikProps.dirty && !formikProps.isSubmitting

        return (
          <div className={classes.root}>
            {/* This is required for React Router navigate-away prevention */}
            <Prompt
              when={preventSubmissionRef.current}
              message='You have unsaved data, are you sure you want to leave?'
            />
            <Paper className={classes.navigation}>
              <Stepper nonLinear activeStep={currentStageId} orientation='horizontal'>
                {stages.map((stage) => (
                  <Step key={stage.id} completed={stage.id !== currentStageId && completeStages.includes(stage.id)}>
                    <StepButton onClick={() => changeStage(stage.id)}>
                      <StepLabel error={stage.id !== currentStageId && errorStages.includes(stage.id)}>
                        {stage.title}
                      </StepLabel>
                    </StepButton>
                  </Step>
                ))}
              </Stepper>
            </Paper>
            <div ref={rootRef}>
              {/* Explanation: This should be fine as we aren't hiding behaviour that can't be accessed otherwise. */}
              {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
              <form className={classes.form} onSubmit={formikProps.handleSubmit} noValidate>
                {/* Prevent implicit submission of the form on enter. */}
                {/* See https://stackoverflow.com/a/51507806 */}
                <button type='submit' disabled style={{ display: 'none' }} aria-hidden='true'></button>
                {currentStageId === StageId.Beginning && (
                  <div className={classes.formPart}>
                    <Paper className={classes.paper}>
                      <Beginning />
                    </Paper>
                    <div className={classes.formPartActions}>
                      <Button onClick={nextStage} variant='contained' color='primary'>
                        Begin
                      </Button>
                    </div>
                  </div>
                )}
                {currentStageId === StageId.BasicInfo && (
                  <div className={classes.formPart}>
                    <Paper className={classes.paper}>
                      <BasicInfo completionBag={completionBag} />
                    </Paper>
                    <div className={classes.formPartActions}>
                      <Button onClick={prevStage}>Previous</Button>
                      <Button onClick={nextStage} variant='contained' color='primary'>
                        Next
                      </Button>
                    </div>
                  </div>
                )}
                {currentStageId === StageId.Audience && (
                  <div className={classes.formPart}>
                    <Paper className={classes.paper}>
                      <Audience {...{ formikProps, indexedSegments, completionBag }} />
                    </Paper>
                    <div className={classes.formPartActions}>
                      <Button onClick={prevStage}>Previous</Button>
                      <Button onClick={nextStage} variant='contained' color='primary'>
                        Next
                      </Button>
                    </div>
                  </div>
                )}
                {currentStageId === StageId.Metrics && (
                  <div className={classes.formPart}>
                    <Paper className={classes.paper}>
                      <Metrics {...{ indexedMetrics, completionBag, formikProps }} />
                    </Paper>
                    <div className={classes.formPartActions}>
                      <Button onClick={prevStage}>Previous</Button>
                      <Button onClick={nextStage} variant='contained' color='primary'>
                        Next
                      </Button>
                    </div>
                  </div>
                )}
                {currentStageId === StageId.Submit && (
                  <div className={classes.formPart}>
                    <Paper className={classes.paper}>
                      <Typography variant='h4' gutterBottom>
                        Confirm and Submit Your Experiment
                      </Typography>
                      <Typography variant='body2' gutterBottom>
                        Now is a good time to{' '}
                        <Link href='https://github.com/Automattic/experimentation-platform/wiki' target='_blank'>
                          check our wiki&apos;s experiment creation checklist
                        </Link>{' '}
                        and confirm everything is in place.
                      </Typography>

                      <Typography variant='body2' gutterBottom>
                        Once you submit your experiment it will be set to staging, where it can be edited up until you
                        set it to running.
                      </Typography>
                      <Typography variant='body2' gutterBottom>
                        <strong> When you are ready, click the Submit button below.</strong>
                      </Typography>
                    </Paper>
                    <GeneralErrorAlert error={formSubmissionError} />
                    <div className={classes.formPartActions}>
                      <Button onClick={prevStage}>Previous</Button>
                      <LoadingButtonContainer isLoading={formikProps.isSubmitting}>
                        <Button
                          type='submit'
                          variant='contained'
                          color='secondary'
                          disabled={formikProps.isSubmitting || errorStages.length > 0}
                        >
                          Submit
                        </Button>
                      </LoadingButtonContainer>
                    </div>
                  </div>
                )}
              </form>
            </div>
          </div>
        )
      }}
    </Formik>
  )
}
Example #5
Source File: SQFormDialogStepper.tsx    From SQForm with MIT License 4 votes vote down vote up
export function SQFormDialogStepper<Values extends FormikValues>({
  cancelButtonText = 'Cancel',
  children,
  disableBackdropClick = false,
  isOpen = false,
  isNextDisabled = false,
  maxWidth = 'sm',
  onClose,
  onSubmit,
  title,
  enableReinitialize = false,
  muiGridProps = {},
  dialogProps,
  setValues,
  fullWidth = true,
  contentStyle,
  initialValues,
  ...props
}: SQFormDialogStepperProps<Values>): JSX.Element {
  const steps = children;
  const [activeStep, setActiveStep] = React.useState(0);
  const currentChild = steps[activeStep];
  const [completed, setCompleted] = React.useState<number[]>([]);

  const validationSchema = currentChild.props.validationSchema || null;

  const classes = useStyles({steps});
  const actionsClasses = useActionsStyles();
  const stepperClasses = useStepperStyles();

  const isLastStep = React.useMemo(() => {
    return activeStep === steps.length - 1;
  }, [activeStep, steps]);

  const handleNext = () => {
    setActiveStep(activeStep + 1);
    setCompleted([...completed, activeStep]);
  };

  const handleStep = (step: number) => () => {
    if (completed.includes(step)) {
      setActiveStep(step);
    }
  };

  const handleSubmit = async (
    values: Values,
    helpers: FormikHelpers<Values>
  ) => {
    if (isLastStep) {
      await onSubmit(values, helpers);
    } else {
      setValues && setValues(values);
      handleNext();
    }
  };

  function SubmitButton() {
    const {errors, values, dirty} = useFormikContext<Record<string, unknown>>();

    const isButtonDisabled = React.useMemo(() => {
      if (isNextDisabled) {
        return true;
      }
      if (!validationSchema) {
        return false;
      }
      const currentStepKeys = Object.keys(validationSchema.fields);
      const stepValues = currentStepKeys.every((step) => {
        return !!values[step];
      });

      if (
        !stepValues ||
        currentStepKeys.some((step) => Object.keys(errors).includes(step)) ||
        !dirty
      ) {
        return true;
      }

      return false;
    }, [errors, values, dirty]);

    const primaryButtonText = isLastStep ? 'Submit' : 'Next';
    return (
      <RoundedButton
        type="submit"
        isDisabled={isButtonDisabled}
        title={primaryButtonText}
      >
        {primaryButtonText}
      </RoundedButton>
    );
  }

  const handleClose = (
    event: Record<string, unknown>,
    reason: 'backdropClick' | 'escapeKeyDown' | 'cancelClick'
  ) => {
    if (disableBackdropClick && reason === 'backdropClick') {
      return;
    }

    onClose(event, reason);
  };

  return (
    <Formik
      {...props}
      validationSchema={validationSchema}
      onSubmit={handleSubmit}
      initialValues={initialValues}
      enableReinitialize={enableReinitialize}
    >
      {() => (
        <Dialog
          TransitionComponent={Transition}
          disableBackdropClick={disableBackdropClick}
          maxWidth={maxWidth}
          open={isOpen}
          onClose={handleClose}
          fullWidth={fullWidth}
          {...dialogProps}
        >
          <Form>
            <DialogTitle disableTypography={true}>
              <Typography variant="h4">{title}</Typography>
            </DialogTitle>
            <Divider />
            {steps.length > 1 && (
              <Grid container classes={stepperClasses}>
                <Stepper
                  activeStep={activeStep}
                  classes={classes}
                  alternativeLabel={true}
                >
                  {steps.map((child, index) => (
                    <Step key={`${child.props.label}-${index}`}>
                      <StepButton onClick={handleStep(index)}>
                        <Typography
                          variant="overline"
                          color={index === activeStep ? 'error' : 'initial'} // sets the color to orange if current step
                        >
                          {child?.props.label}
                        </Typography>
                      </StepButton>
                    </Step>
                  ))}
                </Stepper>
              </Grid>
            )}
            <DialogContent
              dividers
              style={{
                paddingTop: '40px',
                paddingBottom: '40px',
                ...contentStyle,
              }}
            >
              <Grid
                {...muiGridProps}
                container
                spacing={muiGridProps.spacing ?? 3}
                justifyContent="center"
              >
                {currentChild}
              </Grid>
            </DialogContent>
            <DialogActions classes={actionsClasses}>
              <RoundedButton
                title={cancelButtonText}
                onClick={onClose}
                color="secondary"
                variant="outlined"
              >
                {cancelButtonText}
              </RoundedButton>
              <SubmitButton />
            </DialogActions>
          </Form>
        </Dialog>
      )}
    </Formik>
  );
}
Example #6
Source File: QuickStartView.tsx    From SeeQR with MIT License 4 votes vote down vote up
QuickStartView = ({ show }: QuickStartViewProps) => {
  if (!show) return null;
  const classes = useStyles();
  const [activeStep, setActiveStep] = useState(0);
  const [completed, setCompleted] = useState(new Set<number>());
  const [skipped, setSkipped] = useState(new Set<number>());
  const steps = getSteps();

  const totalSteps = () => getSteps().length;

  const isStepOptional = (step: number) => step === null;

  const handleSkip = () => {
    if (!isStepOptional(activeStep)) {
      // You probably want to guard against something like this
      // it should never occur unless someone's actively trying to break something.
      throw new Error("You can't skip a step that isn't optional.");
    }

    setActiveStep((prevActiveStep) => prevActiveStep + 1);
    setSkipped((prevSkipped) => {
      const newSkipped = new Set(prevSkipped.values());
      newSkipped.add(activeStep);
      return newSkipped;
    });
  };

  const skippedSteps = () => skipped.size;

  const completedSteps = () => completed.size;

  const allStepsCompleted = () =>
    completedSteps() === totalSteps() - skippedSteps();

  const isLastStep = () => activeStep === totalSteps() - 1;

  const handleNext = () => {
    const newActiveStep =
      isLastStep() && !allStepsCompleted()
        ? // It's the last step, but not all steps have been completed
          // find the first step that has been completed
          steps.findIndex((step, i) => !completed.has(i))
        : activeStep + 1;

    setActiveStep(newActiveStep);
  };

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

  const handleStep = (step: number) => () => {
    setActiveStep(step);
  };

  const handleComplete = () => {
    const newCompleted = new Set(completed);
    newCompleted.add(activeStep);
    setCompleted(newCompleted);

    if (completed.size !== totalSteps() - skippedSteps()) {
      handleNext();
    }
  };

  const handleReset = () => {
    setActiveStep(0);
    setCompleted(new Set<number>());
    setSkipped(new Set<number>());
  };

  const isStepSkipped = (step: number) => skipped.has(step);

  function isStepComplete(step: number) {
    return completed.has(step);
  }

  return (
    <PageContainer>
      <StyledTypographyTitle align="center">
        Welcome to SeeQR
      </StyledTypographyTitle>
      <img className={classes.image} src={logo} alt="Logo" />
      <StyledStepper alternativeLabel nonLinear activeStep={activeStep}>
        {steps.map((label, index) => {
          const stepProps: { completed?: boolean } = {};
          const buttonProps: { optional?: React.ReactNode } = {};
          if (isStepOptional(index)) {
            buttonProps.optional = (
              <Typography variant="caption">Optional</Typography>
            );
          }
          if (isStepSkipped(index)) {
            stepProps.completed = false;
          }
          return (
            <Step key={label} {...stepProps}>
              <StepButton
                onClick={handleStep(index)}
                completed={isStepComplete(index)}
                {...buttonProps}
              >
                <StyledStepLabel className={classes.stepper}>
                  {label}
                </StyledStepLabel>
              </StepButton>
            </Step>
          );
        })}
      </StyledStepper>
      <div>
        {allStepsCompleted() ? (
          <div>
            <StyledTypographyInstructions className={classes.instructions}>
              All steps completed - you&apos;re ready to use SeeQr!
            </StyledTypographyInstructions>
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <StepContent>
            {getStepContent(activeStep)}
            <NavButtons>
              <Button
                disabled={activeStep === 0}
                onClick={handleBack}
                className={classes.button}
              >
                Back
              </Button>
              <Button
                variant="contained"
                color="primary"
                onClick={handleNext}
                className={classes.button}
              >
                Next
              </Button>
              {isStepOptional(activeStep) && !completed.has(activeStep) && (
                <Button
                  variant="contained"
                  color="primary"
                  onClick={handleSkip}
                  className={classes.button}
                >
                  Skip
                </Button>
              )}
              {activeStep !== steps.length &&
                (completed.has(activeStep) ? (
                  <Typography variant="caption" className={classes.completed}>
                    Step
                    {` ${activeStep + 1} `}
                    already completed
                  </Typography>
                ) : (
                  <Button
                    variant="contained"
                    color="primary"
                    onClick={handleComplete}
                  >
                    {completedSteps() === totalSteps() - 1
                      ? 'Finish'
                      : 'Complete Step'}
                  </Button>
                ))}
            </NavButtons>
          </StepContent>
        )}
      </div>
    </PageContainer>
  );
}