Material UI is a Material Design library made for React.
It’s a set of React components that have Material Design styles.
In this article, we’ll look at how to customize steppers with Material UI.
Non-Linear Steppers
We can add non-linear steppers to add a stepper that allows users to navigate to any step they want.
For example, we can write:
import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepButton from "@material-ui/core/StepButton";
import Button from "@material-ui/core/Button";
function getSteps() {
return ["step 1", "step 2", "step 3"];
}
function getStepContent(step) {
switch (step) {
case 0:
return "do step 1";
case 1:
return "do step 2";
case 2:
return "do step 3";
default:
return "unknown step";
}
}
export default function App() {
const [activeStep, setActiveStep] = React.useState(0);
const [completed, setCompleted] = React.useState({});
const steps = getSteps();
const totalSteps = () => {
return steps.length;
};
const completedSteps = () => {
return Object.keys(completed).length;
};
const isLastStep = () => {
return activeStep === totalSteps() - 1;
};
const allStepsCompleted = () => {
return completedSteps() === totalSteps();
};
const handleNext = () => {
const newActiveStep =
isLastStep() && !allStepsCompleted()
? steps.findIndex((step, i) => !(i in completed))
: activeStep + 1;
setActiveStep(newActiveStep);
};
const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
};
const handleStep = step => () => {
setActiveStep(step);
};
const handleComplete = () => {
const newCompleted = completed;
newCompleted[activeStep] = true;
setCompleted(newCompleted);
handleNext();
};
const handleReset = () => {
setActiveStep(0);
setCompleted({});
};
return (
<div>
<Stepper nonLinear activeStep={activeStep}>
{steps.map((label, index) => (
<Step key={label}>
<StepButton
onClick={handleStep(index)}
completed={completed[index]}
>
{label}
</StepButton>
</Step>
))}
</Stepper>
<div>
{allStepsCompleted() ? (
<div>
All steps completed - you're finished
<Button onClick={handleReset}>Reset</Button>
</div>
) : (
<div>
{getStepContent(activeStep)}
<div>
<Button disabled={activeStep === 0} onClick={handleBack}>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext}>
Next
</Button>
{activeStep !== steps.length &&
(completed[activeStep] ? (
`Step {activeStep + 1} already completed`
) : (
<Button
variant="contained"
color="primary"
onClick={handleComplete}
>
{completedSteps() === totalSteps() - 1
? "Finish"
: "Complete Step"}
</Button>
))}
</div>
</div>
)}
</div>
</div>
);
}
We add the nonLinear
prop to let users click on any step they want.
The Next button calls handleNext
to go to the next step or go to the step that isn’t completed.
handleBack
is run when the back button is clicked.
It sets the step to a previous step.
handleStep
is run when we click on a step.
We also set the completed
prop to indicate which step is completed.
handleReset
is run when the reset button is clicked.
It sets the activeStep
state to 0 and set the completed
state to an empty object to clear the completed steps.
For example, we can write:
import React from "react";
import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepButton from "@material-ui/core/StepButton";
import Button from "@material-ui/core/Button";
function getSteps() {
return ["step 1", "step 2", "step 3"];
}
function getStepContent(step) {
switch (step) {
case 0:
return "do step 1";
case 1:
return "do step 2";
case 2:
return "do step 3";
default:
return "unknown step";
}
}
export default function App() {
const [activeStep, setActiveStep] = React.useState(0);
const [completed, setCompleted] = React.useState(new Set());
const [skipped, setSkipped] = React.useState(new Set());
const steps = getSteps();
const totalSteps = () => {
return getSteps().length;
};
const isStepOptional = step => {
return step === 1;
};
const handleSkip = () => {
if (!isStepOptional(activeStep)) {
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 = () => {
return skipped.size;
};
const completedSteps = () => {
return completed.size;
};
const allStepsCompleted = () => {
return completedSteps() === totalSteps() - skippedSteps();
};
const isLastStep = () => {
return activeStep === totalSteps() - 1;
};
const handleNext = () => {
const newActiveStep =
isLastStep() && !allStepsCompleted()
? steps.findIndex((step, i) => !completed.has(i))
: activeStep + 1;
setActiveStep(newActiveStep);
};
const handleBack = () => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
};
const handleStep = step => () => {
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());
setSkipped(new Set());
};
const isStepSkipped = step => {
return skipped.has(step);
};
function isStepComplete(step) {
return completed.has(step);
}
return (
<div>
<Stepper alternativeLabel nonLinear activeStep={activeStep}>
{steps.map((label, index) => {
const stepProps = {};
const buttonProps = {};
if (isStepOptional(index)) {
buttonProps.optional = `Optional`;
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} {...stepProps}>
<StepButton
onClick={handleStep(index)}
completed={isStepComplete(index)}
{...buttonProps}
>
{label}
</StepButton>
</Step>
);
})}
</Stepper>
<div>
{allStepsCompleted() ? (
<div>
All steps completed
<Button onClick={handleReset}>Reset</Button>
</div>
) : (
<div>
{getStepContent(activeStep)}
<div>
<Button disabled={activeStep === 0} onClick={handleBack}>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext}>
Next
</Button>
{isStepOptional(activeStep) && !completed.has(activeStep) && (
<Button
variant="contained"
color="primary"
onClick={handleSkip}
>
Skip
</Button>
)}
{activeStep !== steps.length &&
(completed.has(activeStep) ? (
`Step ${activeStep + 1} already completed`
) : (
<Button
variant="contained"
color="primary"
onClick={handleComplete}
>
{completedSteps() === totalSteps() - 1
? "Finish"
: "Complete Step"}
</Button>
))}
</div>
</div>
)}
</div>
</div>
);
}
We have our Step
and StepButton
to add the step button.
We apply all the props and allow users to click on each link regardless of status.
buttonProps.optioal
lets us render a string or component to indicate that the step is optional.
We can also set the completed
property of stepProps
to false
.
Conclusion
We can customize non-linear steppers to our liking.
The labels and content can be changed with various components.
One reply on “Material UI — Stepper Customization”
Atleast provide some output ui, Just blank code