Material-UI: Expand Accordion by clicking the icon only - css

I have created an Accordion for my Project. But I want the panel to be expanded only when the expand icon is clicked. Currently it is expanding on clicking anywhere on the panel. Can we customize its expanding behavior ?
CODE:
import React from "react";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import Accordion from "#material-ui/core/Accordion";
import AccordionDetails from "#material-ui/core/AccordionDetails";
import Typography from "#material-ui/core/Typography";
import AccordionSummary from "#material-ui/core/AccordionSummary";
export default function App() {
return (
<div style={{}}>
<h4>How to create Accordion in ReactJS?</h4>
<Accordion style={{ width: 400}}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
>
<div>Click to Expand</div>
</AccordionSummary>
<AccordionDetails>
<Typography>Greetings of the day :)</Typography>
</AccordionDetails>
</Accordion>
</div>
);
}

Yes we can do it like this
1- Create our own state with handler:
const [expand, setExpand] = React.useState(false);
const toggleAcordion = () => {
setExpand((prev) => !prev);
};
2- Add our state to the Accordion:
<Accordion expanded={expand} style={{ width: 400 }}>
3- Add onClick for the icon:
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
IconButtonProps={{
onClick: toggleAcordion
}}
>
Full Code:
EDIT: Explanation: Material-UI Accordion has its own state (open or close) and its own click handler, what we did above is that we create our own state and override Material-UI Accordion state by the prop expanded and add event listener onClick to the icon button by the prop IconButtonProps, our event listener will open or close the Accordion by change our state.
NOTE: The code above doesn't change the style in case of the cursor pointer.

There is no public API to do what you want out-of-the-box, but you can use this CSS trick:
V5
<AccordionSummary
sx={{
pointerEvents: "none"
}}
expandIcon={
<ExpandMoreIcon
sx={{
pointerEvents: "auto"
}}
/>
}
>
V4
const useStyles = makeStyles({
summary: {
pointerEvents: 'none',
},
icon: {
pointerEvents: 'auto',
},
});
<AccordionSummary
className={classes.summary}
expandIcon={<ExpandMoreIcon className={classes.icon} />}
>
<Typography>Accordion 1</Typography>
</AccordionSummary>
Live Demo

For v5, and setting the mouse cursor properly:
1- Create our own state with handler:
const [accordionOpen, setAccordionOpen] = React.useState(false);
2- Add our state to the Accordion and toggle on click:
<Accordion expanded={accordionOpen}>
<AccordionSummary
expandIcon={
<ExpandMoreIcon
style={{ cursor: 'pointer' }}
onClick={() => setAccordionOpen(!accordionOpen)} />
}
sx={{ cursor: 'unset !important' }}
>

None of these solutions worked for me. I handled it with the e.stopPropagation() method, using it in the onClick event of the icon after calling the toggle function.
Like this:
import React from "react";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import Accordion from "#material-ui/core/Accordion";
import AccordionDetails from "#material-ui/core/AccordionDetails";
import Typography from "#material-ui/core/Typography";
import AccordionSummary from "#material-ui/core/AccordionSummary";
export default function App() {
return (
<div style={{}}>
<h4>How to create Accordion in ReactJS?</h4>
<Accordion style={{ width: 400}}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
>
<div ><img src='yourIcon' onClick={(e) => { toggleAccordion(); e.stopPropagation() }} /></div>
</AccordionSummary>
<AccordionDetails>
<Typography>Greetings of the day :)</Typography>
</AccordionDetails>
</Accordion>
</div>
);
}

you can try set expanded true/false in the Accordion
function Container(props) {
const [expand, setExpand] = React.useState(false);
const toggleAccordion = () => {
setExpand((prev) => !prev);
};
return (
<React.Fragment>
<Accordion expanded={expand}>
<AccordionSummary
expandIcon={<ExpandMoreIcon
onClick={toggleAccordion}
/>}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography></Typography>
</AccordionSummary>
<AccordionDetails>
</AccordionDetails>
</Accordion>
</React.Fragment>)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Related

makestyles is not working nextsjs material ui version 5

I am using nextjs with material ui
"#mui/material": "^5.0.1",
"#mui/styles": "^5.0.1",
and here is my component
import { AppBar, Toolbar, Typography, Box, Button, Link } from "#mui/material";
import { makeStyles } from "#mui/styles";
const Navbar = () => {
const useStyles = makeStyles(() => ({
navItem: {
color: "white"
}
}));
const classes = useStyles();
return (
<>
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
MyCode
</Typography>
<Typography variant="h6" component="div">
<Link className={classes.navItem} href="/blog">Blog</Link>
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
</Box>
</>
)
}
export default Navbar;
I have created this navbar with a blog nav item and with login button.
Blog nav item is not visible on the screen as the color it inherit the color of background of nav bar.
So I added some classes by using makeStyles and created a navItem class with color:white
It shows the class on the DOM on html but this is not visible in Styles section, so color is not white.
what can be the issue?
You should define useStyles outside your functional component. In this case you don't need use makeStyles. Just:
<Link color="white" />

Material-UI: Prevent Accordion summary vertical movement

Created this accordion and will use it as a menu item.
However, when I click Main title, accordion summary moves vertically downward.
How can I keep Main tile fixed while opening?
sandbox
import React from "react";
import {
Typography,
Grid,
Accordion,
AccordionSummary,
AccordionDetails,
ListItem
} from "#material-ui/core";
import { createStyles, makeStyles, Theme } from "#material-ui/core/styles";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
panelSummary: {
flexDirection: "row-reverse",
paddingLeft: "0px"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
},
innerMenuItem: {
paddingLeft: "32px"
},
expanded: {
padding: "0px"
}
})
);
export default function App() {
const classes = useStyles();
return (
<Accordion>
<AccordionSummary
className={classes.panelSummary}
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography className={classes.heading}>Main title</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid container direction="column">
<ListItem className={classes.innerMenuItem} button key={1}>
<Typography className={classes.heading}>Sub Item 1</Typography>
</ListItem>
<ListItem
className={classes.innerMenuItem}
button
key={2}>
<Typography className={classes.heading}>Sub Item 2</Typography>
</ListItem>
</Grid>
</AccordionDetails>
</Accordion>
);
}
You can just pass in disableGutters as true in version 5 of mui
<Accordion disableGutters>
// ....
</Accordion>
When expanded, the summary's vertical margin is set to some value, you need to reset it if you don't want to see the summary size changes during the expansion:
V5
<AccordionSummary
sx={{
"&.Mui-expanded": {
minHeight: 0
},
"& .MuiAccordionSummary-content.Mui-expanded": {
// margin from https://github.com/mui-org/material-ui/blob/cc0e2ab63e8be9ec4d51a49bfde17ef28fc77b9c/packages/mui-material/src/AccordionSummary/AccordionSummary.js#L64-L64
margin: "12px 0"
}
}}
>
V4
panelSummary: {
"&.Mui-expanded": {
minHeight: 0
},
"& .MuiAccordionSummary-content.Mui-expanded": {
margin: "auto"
}
},

Reactjs material ui Select item inside dialog with weird style

I am trying to create a "Dialog" box which contain a "Select" inside it.
As you can see from image "Select" component is showing item so weird!
I tried to style the items but I couldn't find the way to show item regular (Vertical items with white background color and black foreground color).
I have to mention that I copied the exact code in material ui website for "Dialog" and
"Select"
When I use "Select" inside "Dialog" this weird style happen.
This is my code:
import React, {useState} from 'react';
import {makeStyles, withStyles} from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import MuiDialogTitle from '#material-ui/core/DialogTitle';
import MuiDialogContent from '#material-ui/core/DialogContent';
import MuiDialogActions from '#material-ui/core/DialogActions';
import IconButton from '#material-ui/core/IconButton';
import CloseIcon from '#material-ui/icons/Close';
import Typography from '#material-ui/core/Typography';
import Card from "#material-ui/core/Card";
import CardActionArea from "#material-ui/core/CardActionArea";
import CardMedia from "#material-ui/core/CardMedia";
import CardContent from "#material-ui/core/CardContent";
import CardActions from "#material-ui/core/CardActions";
import {Col, Row} from "react-bootstrap";
import {FormControl, InputLabel, MenuItem, Select, TextField} from "#material-ui/core";
const styles = (theme) => ({
root: {
margin: 0,
padding: theme.spacing(2),
},
closeButton: {
position: 'absolute',
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500],
},
});
const DialogTitle = withStyles(styles)((props) => {
const { children, classes, onClose, ...other } = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton aria-label="close" className={classes.closeButton} onClick={onClose}>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
const DialogContent = withStyles((theme) => ({
root: {
padding: theme.spacing(2),
},
}))(MuiDialogContent);
const DialogActions = withStyles((theme) => ({
root: {
margin: 0,
padding: theme.spacing(1),
},
}))(MuiDialogActions);
export default function SdWanDialog(props) {
const {
status,
handleClose,
company
} = props;
const useStyles2 = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(1),
minWidth: 185,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
const classes2 = useStyles2();
const [subnet, setSubnet] = useState('');
const handleChange = (event) => {
setSubnet(event.target.value);
};
return (
<div>
{/*<Button variant="outlined" color="primary" onClick={handleClickOpen}>*/}
{/* Open dialog*/}
{/*</Button>*/}
<Dialog onClose={handleClose} aria-labelledby="customized-dialog-title" open={status}>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
TITLE
</DialogTitle>
<DialogContent >
<Row>
<Col>
<FormControl variant="outlined">
<InputLabel id="select-label">Subnet</InputLabel>
<Select
labelId="select-label"
id="select-id"
value={subnet}
onChange={handleChange}
label="Subnet"
>
<MenuItem value=""><em>None</em></MenuItem>
<MenuItem value={'value1'}>value1</MenuItem>
<MenuItem value={'value2'}>value2</MenuItem>
<MenuItem value={'value3'}>value3</MenuItem>
</Select>
<br/>
<TextField
id="branchName"
label="Branch name"
variant="outlined"
color="primary"
/>
</FormControl>
</Col>
</Row>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} variant="contained" color="primary">
Save changes
</Button>
</DialogActions>
</Dialog>
</div>
);
}

AppBar overlaps with other elements

I am starting to use React/Material-UI, and also new to CSS etc...
I have a simple page layout with an APPBar. Unfortunately this AppBar overlaps the elements which are meant to go below it.
I have found this answer:
AppBar Material UI questions
But this feels completely wrong. What if my AppBar has a variable height, depending on the icons, display modes etc...?
I have tried to create a vertical grid, to wrap the elements in different items, made the top container a flex one and play with flex settings, nothing seems to work, the app bar always sits on top of the text.
The code is very simple:
import React from 'react';
import { AppBar, Typography, Box } from '#material-ui/core';
function App() {
return (
<div>
<AppBar>
<Typography variant='h3'>
AppBar
</Typography>
</AppBar>
<Box>
<Typography variant='h1' style={{ border: '1px solid black' }}>
Hello
</Typography>
</Box>
</div>
)
}
export default App;
The "Hello" text chunk is only half visible:
This is happening because the MaterialUI App Bar defaults to position="fixed". This separates it from the standard DOM's layout to allow content to scroll beneath it, but as a result no space is made for it on the page.
You can get around this by wrapping all content below it in a div and specifying enough margin, or by changing the position property of <AppBar> so it's no longer "fixed". In your example, you could also just apply the styles to <Box> if that's the only content below the <AppBar>.
e.g.
import React from 'react';
import { AppBar, Typography, Box } from '#material-ui/core';
function App() {
return (
<div>
<AppBar>
<Typography variant='h3'>
AppBar
</Typography>
</AppBar>
<div style={{marginTop: 80}}>
<Box>
<Typography variant='h1' style={{ border: '1px solid black' }}>
Hello
</Typography>
</Box>
</div>
</div>
)
}
export default App;
MaterialUI provides a theme mixin for the AppBar that can help. Not sure if you're using the recomended JSS setup, but you can do something like this:
import withStyles from '#material-ui/core/styles/withStyles';
const styles = theme => ({
appBarSpacer: theme.mixins.toolbar
});
const style = withStyles(styles)
function MyScreen ({ classes }) {
<AppBar></AppBar>
<div className={classes.appBarSpacer}></div>
<Box></Box>
}
export default style(MyScreen)
The mixin will give that div the same height as your AppBar, pushing down the other content.
According to Material-ui, there are 3 solutions to this problem.
https://material-ui.com/components/app-bar/#fixed-placement
You can use position="sticky" instead of fixed. ⚠️ sticky is not supported by IE 11.
You can render a second component
You can use theme.mixins.toolbar CSS
I personally enjoy using the 2nd solution like this.
return (
<>
<AppBar position="fixed">
<Toolbar>{/* content */}</Toolbar>
</AppBar>
<Toolbar />
</>
);
<AppBar position='static'>
use this it will do it and content won't hide under Appear
I think having a good app setup is opinianted, but I would recommend the following
import React from "react";
import ReactDOM from "react-dom";
import {
AppBar,
Typography,
Box,
CssBaseline,
makeStyles,
Container,
Grid,
Toolbar
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
content: {
flexGrow: 1,
height: "100vh",
overflow: "auto"
},
appBarSpacer: theme.mixins.toolbar,
title: {
flexGrow: 1
},
container: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4)
}
}));
function App() {
const classes = useStyles();
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="absolute">
<Toolbar className={classes.toolbar}>
<Typography
component="h1"
variant="h6"
color="inherit"
noWrap
className={classes.title}
>
AppBar
</Typography>
</Toolbar>
</AppBar>
<main className={classes.content}>
<div className={classes.appBarSpacer} />
<Container maxWidth="lg" className={classes.container}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Box>
<Typography variant="h1" style={{ border: "1px solid black" }}>
Hello
</Typography>
</Box>
</Grid>
</Grid>
</Container>
</main>
</div>
);
}
try this!
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
[theme.breakpoints.down('sm')]: {
marginBottom: 56,
},
[theme.breakpoints.up('sm')]: {
marginBottom: 64,
},
},
menuButton: {
marginRight: theme.spacing(1),
},
title: {
flexGrow: 1,
}, }))
You can add the above to your code like this
const Navbar = () => {
const classes = useStyles()
return (
<div className={classes.root}>
<AppBar position='fixed' color='primary'>
<Toolbar>
<IconButton
edge='start'
className={classes.menuButton}
color='inherit'
aria-label='menu'>
<MenuIcon />
</IconButton>
<Typography variant='h6' className={classes.title}>
News
</Typography>
<Button color='inherit'>Login</Button>
</Toolbar>
</AppBar>
</div>
)}
For more documentation visit material-ui breakpoint customization

Extra Line coming inside Textfield component of material-ui with outlined borders

I am trying to use Textfield component of material ui package. I am using variant="outlined" property of it. But inside that Textfield line is also coming so looks bad from ui prespective.
Any idea how to show only ouline boundary.
(source: imggmi.com)
I guess Textfield by default puts that line. Its is the line just below Phone*.
import React, { Component } from "react";
import { connect } from "react-redux";
import { signIn } from "../../store/actions/authActions";
import { trySignUp } from "../../store/actions/authActions";
import { Redirect } from "react-router-dom";
import Container from "#material-ui/core/Container";
import CssBaseline from "#material-ui/core/CssBaseline";
import Avatar from "#material-ui/core/Avatar";
import Typography from "#material-ui/core/Typography";
import LockOutlinedIcon from "#material-ui/icons/LockOutlined";
import { withStyles } from "#material-ui/styles";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import Checkbox from "#material-ui/core/Checkbox";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
class SignIn extends Component {
render() {
const { authError, auth, classes } = this.props;
if (auth.uid) return <Redirect to="/" />;
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form onSubmit={this.handleSubmit} className={classes.form}>
{!this.state.isOTPSent && (
<TextField
variant="outlined"
margin="normal"
required
fullWidth
type="tel"
id="phone"
label="Phone"
name="phone"
autoComplete="phone"
autoFocus
onChange={this.handleChange}
/>
)}
{this.state.isOTPSent && (
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="otp"
label="OTP"
name="otp"
autoFocus
onChange={this.handleChange}
/>
)}
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<div className="input-field">
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
id="loginButtonId"
onClick={this.state.isOTPSent ? this.handleSubmit : null}
className={classes.submit}
>
{this.state.isOTPSent ? "Login" : "Get OTP"}
</Button>
<div className="red-text center">
{authError ? <p>{authError}</p> : null}
</div>
</div>
</form>
</div>
</Container>
);
}
}
const styles = theme => ({
"#global": {
body: {
backgroundColor: "white"
}
},
paper: {
marginTop: 50,
display: "flex",
flexDirection: "column",
alignItems: "center"
},
avatar: {
margin: 1,
backgroundColor: "red"
},
form: {
width: "100%", // Fix IE 11 issue.
marginTop: 1
},
submit: {
margin: (3, 0, 2)
}
});
const mapStateToProps = state => {
return {
authError: state.auth.authError,
auth: state.firebase.auth
};
};
const mapDispatchToProps = dispatch => {
return {
signIn: phoneNumber => dispatch(signIn(phoneNumber)),
trySignUp: () => dispatch(trySignUp())
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(SignIn));
Need to remove the line.

Resources