Stepper component in latest Material UI - reactjs

I want to use Material UI horizontal steppers. But I am not able to add content to it. Hence, I thought I'll switch to Bootstrap. I've gone through the documentation and examples (https://getbootstrap.com/docs/5.2/examples/) but did not find any implementation for Stepper. Is there any other package I can use for this? Or is there a way to implement content with Material UI horizontal stepper to achieve the purpose.
This is my current implementation, but it's just not right!
import * as React from 'react';
import Box from '#mui/material/Box';
import Stepper from '#mui/material/Stepper';
import Step from '#mui/material/Step';
import StepLabel from '#mui/material/StepLabel';
import StepContent from '#mui/material/StepContent';
import Button from '#mui/material/Button';
import Typography from '#mui/material/Typography';
import FirstStep from './FirstStep';
import { Grid } from '#mui/material';
const steps = [1, 2, 3];
function getStepContent(number) {
switch (number) {
case 0:
return (
<>
<Grid align="center">
<form class="form-group">
<label>First Name</label>
<input type="text" placeholder="First Name"></input>
<br></br>
<label>Last Name</label>
<input type="text" placeholder="Last Name"></input>
</form>
</Grid>
</>
);
case 1:
return (
<form class="form-group">
<label>High School Percentage</label>
<input type="number" placeholder="High School Percentage"></input>
<br></br>
<label>Graduation percentage</label>
<input type="number" placeholder="Graduation Percentage"></input>
</form>
);
case 2:
return (
<form class="form-group">
<label>Permanent Address</label>
<input type="text" placeholder="Permanent Address"></input>
<br></br>
<label>Temporary Address</label>
<input type="text" placeholder="Temporary Address"></input>
</form>
);
default:
return '';
}
}
export default function EdenSignUp() {
const [activeStep, setActiveStep] = React.useState(0);
const [skipped, setSkipped] = React.useState(new Set());
const isStepOptional = (step) => {
return step === 1;
};
const isStepSkipped = (step) => {
return skipped.has(step);
};
const handleNext = () => {
let newSkipped = skipped;
if (isStepSkipped(activeStep)) {
newSkipped = new Set(newSkipped.values());
newSkipped.delete(activeStep);
}
setActiveStep((prevActiveStep) => prevActiveStep + 1);
setSkipped(newSkipped);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
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 handleReset = () => {
setActiveStep(0);
};
return (
<Box sx={{ width: '100%' }}>
<Stepper activeStep={activeStep} alternativeLabel='true' >
{steps.map((label, index) => {
const stepProps = {};
const labelProps = {};
if (isStepOptional(index)) {
labelProps.optional = (
<Typography variant="caption"></Typography>
);
}
if (isStepSkipped(index)) {
stepProps.completed = false;
}
return (
<Step key={label} {...stepProps}>
<StepLabel {...labelProps}>
{label}</StepLabel>
{/* <Typography>
{getStepContent(label)}
</Typography> */}
<StepContent orientation = 'horizontal'>
<Typography>{getStepContent(index)}</Typography>
<div >
<div>
<Button
disabled={activeStep === 0}
onClick={handleBack}
// className={classes.button}
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={handleNext}
// className={classes.button}
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length ? (
<React.Fragment>
{/* <Typography sx={{ mt: 2, mb: 1 }}>
All steps completed - you&apos;re finished
</Typography> */}
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2 }}>
<Box sx={{ flex: '1 1 auto' }} />
<Button onClick={handleReset}>Reset</Button>
</Box>
</React.Fragment>
) : (
<React.Fragment>
<Typography sx={{ mt: 2, mb: 1 }}>Step {activeStep + 1}</Typography>
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2 }}>
<Button
color="inherit"
disabled={activeStep === 0}
onClick={handleBack}
sx={{ mr: 1 }}
>
Back
</Button>
<Box sx={{ flex: '1 1 auto' }} />
{isStepOptional(activeStep) && (
<Button color="inherit" onClick={handleSkip} sx={{ mr: 1 }}>
Skip
</Button>
)}
<Button onClick={handleNext}>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</Box>
</React.Fragment>
)}
</Box>
);
}
Thanks in advance!

you can use react-stepper also, See in CodeSandBox

Related

Overriding MUI Stepper

I need to change a Mui Stepper ( which the code works perfetly )
but what I need is a bit different ,
Instead of having this :
I want to get the text under the icon and instead of having a line between tow steps I prefer to have a '<'
Here is the code :
import Box from '#mui/material/Box';
import Stepper from '#mui/material/Stepper';
import Step from '#mui/material/Step';
import StepButton from '#mui/material/StepButton';
import Button from '#mui/material/Button';
import Typography from '#mui/material/Typography';
const steps = ['Select campaign settings', 'Create an ad group', 'Create an ad'];
export default function HorizontalNonLinearStepper() {
const [activeStep, setActiveStep] = React.useState(0);
const [completed, setCompleted] = React.useState<{
[k: number]: boolean;
}>({});
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()
? // It's the last step, but not all steps have been completed,
// find the first step that has been completed
steps.findIndex((step, i) => !(i in completed))
: activeStep + 1;
setActiveStep(newActiveStep);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleStep = (step: number) => () => {
setActiveStep(step);
};
const handleComplete = () => {
const newCompleted = completed;
newCompleted[activeStep] = true;
setCompleted(newCompleted);
handleNext();
};
const handleReset = () => {
setActiveStep(0);
setCompleted({});
};
return (
<Box sx={{ width: '100%' }}>
<Stepper nonLinear activeStep={activeStep}>
{steps.map((label, index) => (
<Step key={label} completed={completed[index]}>
<StepButton color="inherit" onClick={handleStep(index)}>
{label}
</StepButton>
</Step>
))}
</Stepper>
<div>
{allStepsCompleted() ? (
<React.Fragment>
<Typography sx={{ mt: 2, mb: 1 }}>
All steps completed - you&apos;re finished
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2 }}>
<Box sx={{ flex: '1 1 auto' }} />
<Button onClick={handleReset}>Reset</Button>
</Box>
</React.Fragment>
) : (
<React.Fragment>
<Typography sx={{ mt: 2, mb: 1 }}>Step {activeStep + 1}</Typography>
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2 }}>
<Button
color="inherit"
disabled={activeStep === 0}
onClick={handleBack}
sx={{ mr: 1 }}
>
Back
</Button>
<Box sx={{ flex: '1 1 auto' }} />
<Button onClick={handleNext} sx={{ mr: 1 }}>
Next
</Button>
{activeStep !== steps.length &&
(completed[activeStep] ? (
<Typography variant="caption" sx={{ display: 'inline-block' }}>
Step {activeStep + 1} already completed
</Typography>
) : (
<Button onClick={handleComplete}>
{completedSteps() === totalSteps() - 1
? 'Finish'
: 'Complete Step'}
</Button>
))}
</Box>
</React.Fragment>
)}
</div>
</Box>
);
}
Is there a way to override the MUI Stepper styles ?
Thank you in advance
Okay so basically it was 2 steps. The first one was to make the labels appear below the icons which was relatively easy.
I had to add alternativeLabel as a prop to the <Stepper />.
The next step was to remove the lines and replace them with < which wasn't straightforward. I did that by styling the .MuiStepConnector class, replacing its content and removing the border.
<Stepper
nonLinear
alternativeLabel
activeStep={activeStep}
sx={{
".MuiStepConnector-root": {
top: 0
},
".MuiStepConnector-root span": {
borderColor: "transparent"
},
".MuiStepConnector-root span::before": {
display: "flex",
justifyContent: "center",
content: '"<"'
}
}}
>
This is the result:

My react component return statement fails to render but console.log shows exactly what I need

I am new to react and working on a react video player. I'm having issue implementing the comment section.
This is my videoplayer component itself.
export default function VidPlayer() {
// useStates
const [state, setState] = useState({
playing: true,
});
const [comments, setComments] = useState([]);
const [comment, setComment] = useState("");
const { playing } = state;
const playerRef = useRef(null);
// declaring functions for video player buttons
const handlePlayPause = () => {
setState({ ...state, playing: !state.playing });
};
const handleRewind = () => {
playerRef.current.seekTo(playerRef.current.getCurrentTime() - 5);
};
const handleFoward = () => {
playerRef.current.seekTo(playerRef.current.getCurrentTime() + 5);
};
const handleStop = () => {
playerRef.current.seekTo(0);
setState({ playing: !state.playing });
};
// declaring functions for comment section
const addComments = () => {
if (comment) {
setComments({...comments, comment});
setComment("");
console.log("Hello", comments);
}
};
const handleComment = (e) => {
setComment(e.target.value);
};
return (
<div>
<AppBar style={{ background: "#e6880e" }} position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Favour's Video Player
</Typography>
</Toolbar>
</AppBar>
{/* container for the videoplayer, buttons and comment section */}
<div className="container">
<>
{/* videoplayer */}
<div className="reactPlayer one">
<ReactPlayer
ref={playerRef}
url="https://www.youtube.com/watch?v=1_ATK0BLc8U&t=3s"
playing={playing}
controls
/>
</div>
{/* buttons */}
<div className="btn-stack two">
<Stack spacing={2} direction="row">
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handlePlayPause}
>
Play
</Button>
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handleRewind}
>
Rewind{" "}
</Button>
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handleFoward}
>
Forward{" "}
</Button>
<Button
style={{ background: "#e6880e" }}
size="small"
variant="contained"
onClick={handleStop}
>
Stop
</Button>
</Stack>
</div>
{/* comment section */}
<div className="comment three">
<Comment userComs={comments} />
<TextField
placeholder="add comment"
size="small"
variant="outlined"
onChange={handleComment}
value={comment}
/>
<Button
style={{ background: "#e6880e", marginLeft: '1em' }}
onClick={addComments}
variant="contained"
size="small"
>
Send
</Button>
</div>
</>
</div>
</div>
);
}
It takes in this comments component towards the end.
export default function commentList(props) {
console.log("Hello brian", props.userComs);
const { userComs } = props;
if (Object.keys(userComs).length > 0) {
console.log(userComs);
// userComs.forEach((element) => {
// console.log("Im in", userComs);
Object.values(userComs).forEach(val => {
// console.log("Im in", userComs);
// console.log(val);
return (
<div>
<List
sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }}
>
<ListItem alignItems="flex-start">
<ListItemAvatar>
<Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />
</ListItemAvatar>
<ListItemText
secondary={
<React.Fragment>
<Typography
sx={{ display: "inline" }}
component="span"
variant="body2"
color="text.primary"
>
Ali Connors
</Typography>
{val}
</React.Fragment>
}
/>
</ListItem>
<Divider variant="inset" component="li" />
</List>
</div>
);
});
} else {
return <div></div>;
}
}
This is the Front-End enter image description here
Unfortunately, when I enter a comment and click send, the screen goes blank and console throws a "nothing was returned from render" error. Can someone help me check what is wrong and how I can fix this please?
As the error says, the component isn't returning anything.
Object.values(userComs).forEach(val => {
should be
return Object.values(userComs).map(val => {
because forEach doesn't return anything and the JSX returned in each iteration will not be used anywhere, but map returns a new array that React can use.
BTW make sure to give a key prop to each div that is returned from the callback.
<div key={val}> // assuming `val` is unique

Create Stepper, that handle next and back as onClick-event

I want to create a Stepper, where you can handleNext and handleBack in the StepLabel as onClickevent.
I dont have a clue, how to do it.
here is the sandbox: https://codesandbox.io/s/stepper-forked-2q1wd
export default function IndexPage() {
const [activeStep, setActiveStep] = useState(0);
const handleNext = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
function getStepsContent(stepIndex) {
switch (stepIndex) {
case 0:
return <h1>adsf</h1>;
case 1:
return <h1>adsf</h1>;
case 2:
return <h1>adsf</h1>;
}
}
const steps = getSteps();
return (
<>
<div>
<Stepper
alternativeLabel
activeStep={activeStep}
connector={<ColorlibConnector />}
>
{steps.map((label) => (
<Step key={label}>
<StepLabel
onClick={handleNext}
StepIconComponent={ColorlibStepIcon}
>
{label}
</StepLabel>
</Step>
))}
</Stepper>
</div>
<br />
<div>
{getStepsContent(activeStep)}
{activeStep === steps.length ? (
"Bewerbung abgesendet"
) : (
<div style={{ display: "flex", justifyContent: "center" }}>
<div>
<Button
variant="outlined"
color="primary"
onClick={handleBack}
disabled={activeStep === 0}
>
{activeStep > 0 ? "Zurück" : null}
</Button>
<Button
style={{ color: "white", margin: 30 }}
variant="contained"
color="primary"
onClick={handleNext}
>
{activeStep === steps.length - 1 ? "Absenden" : "Weiter"}
</Button>
</div>
</div>
)}
</div>
</>
);
}
I just added the handleNext function as a onClick, but I want also to go back with clicking on the stepper.
You can use the index to set the steps. replace your onClick on your StepLabel with this
{steps.map((label, index) => (
<Step key={label}>
<StepLabel
onClick={() => setActiveStep(index)}
StepIconComponent={ColorlibStepIcon}
>
{label}
</StepLabel>
</Step>
))}
Stepper StepLabel Fix

Material-UI Slider not drag when loading component from a switch

I am working on functionality where there is a set of radio buttons that change the state and determines which components should be active. I think I am close and the values all get set as they should but the slider will not slide, you have to click it in to position. I have a sandbox of what I am talking about here :
https://codesandbox.io/s/elated-banach-k1i4y?file=/src/Form.js
You will see that the radio reveals the appropriate slider. If you click the TEST button you can see that the correct value gets set to the object. I also added another Slider outside of the switch render method and you can see that works as expected.
Also here is the code I am using
import React, { Fragment, useState } from "react";
import {
Radio,
RadioGroup,
FormControlLabel,
FormControl,
Typography,
Slider,
Button
} from "#material-ui/core/";
export default function CreateForm() {
const defaultValues = {
a: 0,
b: 0,
c: 0
};
const [radioValue, setValue] = useState("");
const [activity, setActivity] = useState(defaultValues);
const handleRadioChange = (e) => {
const { value } = e.target;
setValue(value);
setActivity(defaultValues);
};
const handleSlider = (name) => (e, value) => {
setActivity({
...activity,
[name]: value
});
};
function RadioButtonsGroup() {
return (
<RadioGroup
aria-label="group"
name="group"
value={radioValue}
onChange={handleRadioChange}
row
>
<FormControlLabel
name="group"
value="a"
control={<Radio />}
label="A"
/>
<FormControlLabel
name="group"
value="b"
control={<Radio />}
label="B"
/>
</RadioGroup>
);
}
function test() {
console.log(activity);
}
function RenderSwitch(radioValue) {
switch (radioValue.value) {
case "a":
return <GetA />;
case "b":
return <GetB />;
default:
return <p>Select Radio</p>;
}
}
function GetA() {
return (
<React.Fragment>
<Typography id="discrete-slider" gutterBottom>
A
</Typography>
<Slider
value={activity.a}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={120}
name="a"
onChange={handleSlider("a")}
style={{ marginBottom: "20px" }}
/>
</React.Fragment>
);
}
function GetB() {
return (
<Fragment>
<Typography id="discrete-slider" gutterBottom>
B
</Typography>
<Slider
value={activity.b}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={120}
name="a"
onChange={handleSlider("b")}
style={{ marginBottom: "20px" }}
/>
</Fragment>
);
}
return (
<>
<form noValidate onSubmit={(e) => e.preventDefault()}>
<FormControl>
<RadioButtonsGroup />
<RenderSwitch value={radioValue} />
<Button
fullWidth
variant="contained"
color="primary"
type="submit"
onClick={test}
>
TEST
</Button>
<Typography
style={{ marginTop: "40px" }}
id="discrete-slider"
gutterBottom
>
One more to show how drag works here
</Typography>
<Slider
value={activity.c}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={120}
name="c"
onChange={handleSlider("c")}
/>
</FormControl>
</form>
</>
);
}
You have to handle the sliders' state change inside their own elements like so (Sandbox):
const SliderA = () => {
const [activity, setActivity] = useState(0);
const handleChange = (event, newValue) => {
setActivity(newValue);
};
return (
<React.Fragment>
<Typography id="discrete-slider" gutterBottom>
A
</Typography>
<Slider
value={activity}
aria-labelledby="discrete-slider"
valueLabelDisplay="auto"
step={10}
marks
min={10}
max={120}
name="a"
onChange={handleChange}
style={{ marginBottom: "20px" }}
/>
</React.Fragment>
);
};

Pass props to child component from parent component dynamically

I have a child component StepperNotification which gets an input from user and returns it as prop to its child component in following way.
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
}
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
function getSteps() {
return ['Create', 'Audience', 'Timing'];
}
function getStepContent(step, $this) {
switch (step) {
case 0:
return (
<div className="row">
<CardBox styleName="col-lg-12"
heading="">
<form className="row" noValidate autoComplete="off" style={{"flex-wrap":"no-wrap", "flex-direction": "column" }}>
<div className="col-md-12 col-12">
<TextField
id="campaign_name"
label="Campaign Name"
value={$this.state.name}
onChange={$this.handleChange('name')}
margin="normal"
fullWidth
/>
</div>
</form>
</CardBox>
</div>
);
default:
return 'Unknown step';
}
}
class NotificationStepper extends React.Component {
state = {
activeStep: 0,
name: '',
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
this.props.titlechange(event.target.value);
};
handleNext = () => {
this.setState({
activeStep: this.state.activeStep + 1,
});
};
handleBack = () => {
this.setState({
activeStep: this.state.activeStep - 1,
});
};
handleReset = () => {
this.setState({
activeStep: 0,
});
};
render() {
const steps = getSteps();
const {activeStep} = this.state;
return (
<div className="col-xl-12 col-lg-12 col-md-7 col-12">
<Stepper className="MuiPaper-root-custom" activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
return (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent className="pb-3">
<Typography>{getStepContent(index, this)}</Typography>
<div className="mt-2">
<div>
<Button
disabled={activeStep === 0}
onClick={this.handleBack}
className="jr-btn"
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={this.handleNext}
className="jr-btn"
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length && (
<Paper square elevation={0} className="p-2">
<Typography>All steps completed - you"re finished</Typography>
<Button onClick={this.handleReset} className="jr-btn">
Reset
</Button>
</Paper>
)}
</div>
);
}
}
export default NotificationStepper;
In my parent component i am getting this prop value and passing it to another child component Tabcomponent in following way
ParentCompoennt.js
const SendNotification = ({match}) => {
let titlename = '';
let message = '';
function handleTitle(title_) {
console.log('in here');
console.log(title_);
titlename = title_;
}
return (
<div className="dashboard animated slideInUpTiny animation-duration-3">
<ContainerHeader match={match} title={<IntlMessages id="sidebar.notification"/>}/>
<div className="row" style={{'flex-wrap': 'no wrap', "flex-direction": 'row'}}>
<div className="col-xl-7 col-lg-7 col-md-7 col-7">
<NotificationStepper titlechange={handleTitle} />
<div className='flex-class' style={{'width': '100%'}}>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2">Fetch</Button>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2" color="primary">Discard</Button>
</div>
</div>
<div className="col-xl-5 col-lg-5 col-md-5 col-5" style={{"padding-top": "20px"}}>
<span style={{"margin-left" : "20px", "font-weight": "bold"}}>Preview</span>
<TabComponent {...{[title]:titlename}} message={message} />
</div>
</div>
</div>
);
};
export default SendNotification;
and in TabComponent i am getting this prop value and using it component in following way
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
dir: PropTypes.string.isRequired,
};
class TabComponent extends Component {
state = {
value: 0,
};
render() {
const {theme} = this.props;
const title = this.props.title;
return (
<div className="col-xl-12 col-lg-12 col-md-12 col-12" style={{"margin-top": "15px"}}>
<NotifCard key={0} data={{'name': title, 'company': 'sayge.ai', 'image': require("assets/images/bell.png"), 'description': this.props.message}} styleName="card shadow "/>
</div>
);
}
}
TabComponent.propTypes = {
theme: PropTypes.object.isRequired,
};
export default withStyles(null, {withTheme: true})(TabComponent);
StepperNotification is working fine and props are being updated in parent. i have checked this by printing the updated values in console, but the props in TabComponent are not being updated. What am i doing wrong here? any help is appreciated.
Probaly this is your issue
<TabComponent {...{[title]:titlename}} message={message} />` this could be just
title is undefined and you are sending props[undefined]=titlename
Just do this
<TabComponent title={titlename} message={message} />`
And if SendNotification is react fuction component useState for keeping track of current titlename. If it still doesn't work after first fix this second will be another source of your problems.

Resources