Material UI: best way to set elements side by side - reactjs

I'm currently using Material UI to style an ecommerce project. I'm trying to figure out how to make all of the elements (Typography, Buttons) display side by side in a row instead of vertically.
Is there a way to do this simply with material UI? Should I add CSS? I previously tried adding each element to a Grid item within a Container, it kind of worked (couldn't center), but doesn't seem like the right approach for this scenario.
If you see my screenshot below, I'm trying to set "T-Shirt Twilight", the image, "$49.99", the quantity input buttons and "Remove" side by side horizontally.
Elements I'm trying to set side by side:
<div className="App">
{products.map((item, index) => (
<Grid container item xs={12}>
<Grid item xs={1} />
<Grid item xs={10}>
<Grid item xs={12} container key={item.id}>
<Grid item xs={1} />
<Grid item xs={10}>
<Typography>{item.title}</Typography>
<img src={require(`../images/${item.image}`)} className={classes.productImage}></img>
<Typography>${(item.quantity * item.price).toFixed(2)}</Typography>
<ButtonGroup size="small">
<Button type="button" onClick={item.quantity > 1 ? () => decreaseQuantity(index) : null}>-</Button>
<Button>{item.quantity}</Button>
<Button type="button" onClick={() => increaseQuantity(index)}>+</Button>
</ButtonGroup>
<Button
onClick={() => removeItem(index)}>
Remove
</Button>
</Grid>
<Grid item xs={1} />
</Grid>
</Grid>
<Grid item xs={1} />
</Grid>
))}
</div>
Screenshot for reference:
Full code:
import React, { useState, useEffect } from 'react';
import './../App.css';
import * as ReactBootStrap from 'react-bootstrap';
import {Link} from 'react-router-dom';
import { getQuantity, getTotal } from '../helpers/helperTools';
import {Grid, Typography,useMediaQuery, useTheme, Container, Button, ButtonGroup} from '#material-ui/core';
import {makeStyles} from '#material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
productImage: {
maxWidth: '20%'
}
}))
function Cart({ setQty: setParentQty }) {
const classes = useStyles();
const [products, setProducts] = useState([]);
function updateQty(products){
/* var holder = 0;
products.forEach((a, b) => {
holder = holder + a.quantity
})*/
// setQty({quantity: holder})
// localStorage.setItem('quantity', JSON.stringify({ quantity: newQty }))
setParentQty({ quantity: getQuantity(products) });
}
useEffect(function() {
const storageItems = JSON.parse(localStorage.getItem('product'));
const products = storageItems || [];
setProducts(products);
updateQty(products);
}, []);
function decreaseQuantity(index) {
if (products[index]){
const newProducts = products.map((a, b) => {
if (b === index) return {...a, quantity: a.quantity - 1}
else return a
});
setProducts(newProducts);
localStorage.setItem('product', JSON.stringify(newProducts))
updateQty(newProducts)
}
}
function increaseQuantity(index) {
if (!products[index]) return;
const newProducts = products.map((a, b) => {
if (b === index) return {...a, quantity: a.quantity + 1}
else return a
})
setProducts(newProducts)
localStorage.setItem('product', JSON.stringify(newProducts))
updateQty(newProducts);
}
function removeItem(index){
const product = products[index];
if (!product) return;
const newProducts = products.filter((v, z) => z !== index);
setProducts(newProducts);
localStorage.setItem('product', JSON.stringify(newProducts));
updateQty(newProducts);
}
if (products.length === 0) {
return (
<div className="App">
<p>
Cart Empty
</p>
<Link to={`/`}>
<p>Continue shopping</p>
</Link>
</div>)
}
return (
<div className="App">
{products.map((item, index) => (
<Grid container item xs={12}>
<Grid item xs={1} />
<Grid item xs={10}>
<Grid item xs={12} container key={item.id}>
<Grid item xs={1} />
<Grid item xs={10}>
<Typography>{item.title}</Typography>
<img src={require(`../images/${item.image}`)} className={classes.productImage}></img>
<Typography>${(item.quantity * item.price).toFixed(2)}</Typography>
<ButtonGroup size="small">
<Button type="button" onClick={item.quantity > 1 ? () => decreaseQuantity(index) : null}>-</Button>
<Button>{item.quantity}</Button>
<Button type="button" onClick={() => increaseQuantity(index)}>+</Button>
</ButtonGroup>
<Button
onClick={() => removeItem(index)}>
Remove
</Button>
</Grid>
<Grid item xs={1} />
</Grid>
</Grid>
<Grid item xs={1} />
</Grid>
))}
</div>
);
}
export default Cart;

Just use inline style for it. I also removed some unnecessary grid items. May be you want to grid more about breakpoints in Grid. Working CodeSandBox
<Grid container>
<Grid item xs={1} />
<Grid item xs={10} style={{ display: "flex", gap: "1rem" }}>
<Typography>{item.title}</Typography>
<img src={item.image} className={classes.productImage}></img>
<Typography>${(item.quantity * item.price).toFixed(2)}</Typography>
<ButtonGroup size="small">
<Button
type="button"
onClick={
item.quantity > 1 ? () => decreaseQuantity(index) : null
}
>
-
</Button>
<Button>{item.quantity}</Button>
<Button type="button" onClick={() => increaseQuantity(index)}>
+
</Button>
</ButtonGroup>
<Button onClick={() => removeItem(index)}>Remove</Button>
</Grid>
<Grid item xs={1} />
</Grid>

Related

How to get a value using formik with a MUI component?

I am using Formik in order to submit an array of components using MUI sliders.
I have created a slider component using the MUI library. You can see that in the code below in the declaration of:
import SliderSchedule from ....
So, I searched an example and I found a way to create and delete components and I copy from them. However now I want to also submit the result. But when I click the button I get no values.
As you can see I have the Comp1 that I declare the array const [sliderSchedule, setSliderSchedule]. Which has to contain values like: [(100, 200), (150, 300), (400, 700), ...].
Then I have the two function addHandeler and deleteHandeler that creates and deletes the components. This works fine.
Then we have the formik and that's the last thing that I have implemented after creating the the Comp2 that it's the "component" that we are creating with the addHandeler and deleteHandeler which creates the slider and a button to eliminate:
As you can see is very simple. And it works fine. But then when I click submit I get no values:
Thank you in advance.
import React from 'react';
import { useDispatch, useSelector } from "react-redux";
import history from "../../../history";
import { nanoid } from "nanoid";
/* MUI */
import { Container, Grid, Typography, Paper, TextField } from '#material-ui/core'
/* MUI edited Components */
import Appbar from '../../../components/Appbar';
import Button from '../../../components/Button';
import Translator from '../../../components/Translator';
import SliderSchedule from '../../../components/SliderSchedule';
/* Formik */
import { Formik, Form } from 'formik';
import * as Yup from "yup";
export default function Slots() {
const dispatch = useDispatch();
const paperStyle = { padding: 20, height: 'auto', width: 'auto', margin: "20px auto", backgroundColor: "#A9A9A9" }
function Comp2({ deleteHandeler, id, props }) {
return (
<Grid container item xs={12}>
<Grid item xs={11}> <SliderSchedule id={id} name={props.values.name} value={props.values.time} onChange={time => props.setFieldValue("time", time.target.value)} renderInput={(props) => <TextField {...props} />} /> </Grid>
<Grid item xs={1}> <Button type="button" style="Close" onClick={deleteHandeler}>CloseIcon</Button> </Grid>
</Grid>
);
}
const formSchema = {
time: []
};
function Comp1() {
const [sliderSchedule, setSliderSchedule] = React.useState([]);
const addHandeler = () => { setSliderSchedule(sliderSchedule => [...sliderSchedule, nanoid()]) };
const deleteHandeler = (removeId) => setSliderSchedule((sliderSchedule) => sliderSchedule.filter((id) => id !== removeId));
return (
<Formik
initialValues={formSchema}
onSubmit={values => {
console.log("Monday", values);
}}
render={props => (
<Form>
<Grid container justifyContent="center">
{sliderSchedule.map((id) => (<Comp2 key={id} id={id} deleteHandeler={() => deleteHandeler(id)} props={props} />))}
<Button variant="outlined" color="info" style="AddSlider" startIcon="AddIcon" onClick={addHandeler}> <Translator page="productSchedule" text="buttonAddSlider"></Translator> </Button>
<Button type="submit" variant="contained" color="primary"> Submit </Button>
</Grid>
</Form>
)}
/>
);
}
return (
<>
<Appbar></Appbar>
<Container>
<Grid container alignItems="flex-end">
{/* LA SUMA DE TOTS ELS xs={x} ha de ser xs={12} */}
<Grid container justifyContent="flex-start" item xs={12}>
<Typography variant="h3"> <Translator page="productSchedule" text="typographyTitle"></Translator> </Typography>
<Typography variant="h3"> 📅 </Typography>
</Grid>
<Grid item xs={12}>
<Paper elevation={10} style={paperStyle}>
<Grid container alignItems="flex-end">
<Grid container justifyContent="center" item xs="auto"> <Typography variant="h3"> <Translator page="productSchedule" text="typographyMonday"></Translator> </Typography> </Grid>
<Grid container justifyContent="center" item xs="auto"> <Comp1 /> </Grid>
</Grid>
</Paper>
</Grid>
</Grid>
</Container>
</>
);
}

Can you pass different Material-ui icons as props?

I am wondering how I would add different Icons? This is what I have so far but the icon does not show in my UI?
my props thought that would work
When I add the Facebook button it doesn't show like it does on the button I added
const Item = ({ name = 'Item Name', tags = [] }, icon) => {
return (
<Grid item xs={12} spacing={2}>
<Paper elevation={6}>
<Grid container justifyContent="flex-start" spacing={3}>
<Grid item xs={12}>
{/*Notice the name is in {} since it is a variable*/}
<h1>{name}</h1>
{icon}
<div>{icon}</div>
</Grid>
<Grid item>
{tags.map((tag, idx) => {
return <Chip key={idx} label={tag} color={'secondary'} />;
})}
</Grid>
<Grid item>
<Button endIcon={<FacebookIcon />} color="primary" variant="contained">
Button 1
</Button>
<Button color="secondary" variant="contained">
Button 2
</Button>
</Grid>
</Grid>
</Paper>
</Grid>
);
};
const MyView = () => {
return (
<Widget>
<Grid container spacing={4}>
<Item icon={<FacebookIcon />} name={jsonData.name} tags={jsonData.tags} />
<Item name="test paper" tag={'chip2'}></Item>
<Item name={jsonData.name} tags={jsonData.tags} />
</Grid>
</Widget>
);
};
export default MyView;
your mistake is there const Item = ({name="Item Name", tags=[],icon} ) => {
you must import icon from the props object not like a parameter. you should notice that Item was not used like a function with parameter and it is a react component :
import FacebookOutlinedIcon from '#mui/icons-material/FacebookOutlined';
const Item = ({name="Item Name", tags=[],icon} ) => {
return (
<>
<Grid item xs={12} spacing={2}>
<Paper elevation={6}>
<Grid container justifyContent="flex-start" spacing={3}>
<Grid item xs={12}>
{/*Notice the name is in {} since it is a variable*/}
<h1>{name}</h1>
{icon}
<div>{icon}</div>
</Grid>
</Grid>
</Paper>
</Grid>
</>
)
}
const Add = (props) => {
return (
<Item icon={<FacebookOutlinedIcon/>} name="test" tags={[]}/>
);
};
export default Add;

Grid of card in material-ui react

Hello I'm trying to create that I had three cards in a row right now one after the other
Can't find a solution
It's a complement
const Posts = ({ setCurrentId }) => {
const fuzzySearch = (list, searchValue) => {
let buf = ".*" + searchValue.replace(/(.)/g, "$1.*").toLowerCase();
var reg = new RegExp(buf);
let newList = list.filter(function (e) {
return reg.test(e.title.toLowerCase()&&e.message);
});
return newList;
};
const [searchValue, setSearchValue] = useState("");
const posts = useSelector((state) => state.posts);
const classes = useStyles();
const [pageNumber, setPageNumber] = useState(1);
const [buttonnext, setbuttonnext] = useState(false);
const [prebutton, setprebutton] = useState(true);
const limit=8;
const [startIndex,setstartIndex] = useState();
const [endIndex,setendIndex] = useState();
useEffect(()=>{
setstartIndex((pageNumber-1)*limit)
setendIndex(pageNumber*limit)
console.log(startIndex)
console.log(endIndex)
},[posts,pageNumber])
const Next = ()=>{
if(pageNumber === (Math.floor((posts.length+limit -1)/limit))){
setbuttonnext(true)
}else{
setPageNumber(pageNumber+1)
setprebutton(false)
}
}
const Previous = () =>{
if(pageNumber === 1){
setprebutton(true)
}else{
setPageNumber(pageNumber-1)
setbuttonnext(false)
}
}
return(
!posts.length ? <CircularProgress /> : <>
< >
<AppBar className={classes.appBar} position="static" color="inherit">
<Typography className={classes.heading} variant="h2" >קבוצות אחרונות</Typography>
<Paper component="form" className={classes.root}>
<IconButton className={classes.iconButton} aria-label="menu">
</IconButton>
<InputBase
className={classes.input}
placeholder="חיפוש "
inputProps={{ 'aria-label': 'search ' }}
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
/>
<IconButton className={classes.iconButton} aria-label="search">
<SearchIcon />
</IconButton>
<Divider className={classes.divider} orientation="vertical" />
</Paper>
</AppBar>
<ol>
{fuzzySearch(posts, searchValue).slice(startIndex,endIndex).map((d) => (
<Grid key={d._id} item xs={10} sm={6} md={6}>
<Grow in>
<Post post={d} setCurrentId={setCurrentId} />
</Grow>
</Grid>) )}
</ol>
</>
<Grid className={classes.container} container alignItems="stretch" spacing={3}>
<Grid container direction="row-reverse"justifyContent="center"alignItems="flex-end" >
<>
<button className={classes.button} onClick={Next}>הבא</button>
<p className={classes.numText}>{ pageNumber}</p>
<Button className={classes.button} onClick={Previous}>אחורה</Button>
</>
</Grid>
</Grid></>
)
};
export default Posts;
Then called to app in app.js Grid in shape
return (
<Container maxWidth="lg" >
<Grow in>
<Container>
<Posts setCurrentId={setCurrentId} />
</Container>
</Grow>
</Container>
);
};
export default App;
I would to create a grid that I had three in a row
I don't know what you are trying to achieve, but I assume that you are trying to display 3 cards in a row. The idea is that Material-UI is using flexbox, and they are divided into 12 equal columns.
<Grid spacing={2}>
// Define grid for card items
{
data.map((dataItem) => (
<Grid key={dataItem.id}>
// ... items inside
</Grid>
))
}
</Grid>

Reactjs tests (react testing library)

Can anyone help with how I can test the component below? I am using the testing-library / react library and am having difficulties.
export default function BodyCardConfirmacaoSeguranca({ email, celular }) {
const [selectedCard, setSelectedCard] = useState(null);
const handleSelectCard = (value) => {
if (value === selectedCard) {
setSelectedCard(null);
} else {
setSelectedCard(value);
}
};
return (
<>
<CardEnvioCod
data-testid="email"
tipoEnvio="email"
data={email}
handleSelectCard={() => handleSelectCard('email')}
selectedCard={selectedCard}
/>
<CardEnvioCod
data-testid="sms"
tipoEnvio="sms"
data={telefone(celular)}
handleSelectCard={() => handleSelectCard('sms')}
selectedCard={selectedCard}
/>
</>
);
}
I'm trying something like this:
it('', () => {
const { findByTestId } = Render(
<ThemeProvider theme={AppTheme}>
<BodyCardConfirmacaoSeguranca />
</ThemeProvider>,
);
const email = findByTestId('email');
const sms = findByTestId('sms');
fireEvent.change(email, { selectedCard: 'email' });
fireEvent.change(sms, { selectedCard: 'sms' });
});
I need to test the handleSelectCard function and its call on the components
below follows the code, it has not yet been tested. It is used inside the , can I be doing the test in the wrong place too? I'm lost
export default function CardEnvioCod({
tipoEnvio,
data,
handleSelectCard,
selectedCard,
}) {
const classes = useStyles();
const selected = selectedCard === tipoEnvio;
const icon =
tipoEnvio === TIPO_ENVIO.EMAIL ? (
<EmailOutlined
className={clsx(classes.icon, selected && classes.selected)}
/>
) : (
<PhoneAndroidOutlinedIcon
className={clsx(classes.icon, selected && classes.selected)}
/>
);
const text =
tipoEnvio === TIPO_ENVIO.EMAIL
? 'Enviar por e-mail:'
: 'Enviar por SMS:';
useEffect(() => {}, []);
return (
<Grid container justify="center" className={classes.container}>
<Grid
item
component={Paper}
variant="outlined"
lg={7}
xs={12}
square
elevation={0}
className={clsx(classes.root, selected && classes.selected)}
onClick={handleSelectCard}
>
<Grid container justify="space-between" alignItems="center">
<Grid item lg={1} xs={2}>
<Icon>{icon}</Icon>
</Grid>
<Grid item xs={8}>
<Grid container justify="flex-start" direction="column">
<Typography
className={clsx(
classes.titleCard,
selected && classes.selected,
)}
>
{text}
</Typography>
<Typography
title={data}
className={classes.divData}
>
{data}
</Typography>
</Grid>
</Grid>
<Grid item lg={1} xs={2}>
<Grid container justify="flex-end">
{selected ? (
<CheckBoxIcon
fontSize="large"
cursor="pointer"
/>
) : (
<CheckBoxOutlineBlank
color="primary"
fontSize="large"
cursor="pointer"
/>
)}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
);
}

How to use Checkbox from material-UI to toggle strikethrough on a todo list item

const Todo: React.FC<ITodoProps> = (props) => {
const [textInput, setTextInput] = useState('');
const {
addTodo,
userId,
todosForUser,
user,
} = props;
if (user == null) {
return (
<Grid
container={true}
direction='column'
wrap='nowrap'
>
<Grid
item={true}
>
<Typography
variant='h5'
>
INVALID USER
</Typography>
</Grid>
</Grid>
);
}
return (
<Grid
container={true}
direction='column'
wrap='nowrap'
>
<Grid
item={true}
>
<Typography
variant='h5'
>
TODOS FOR {user.get('name')}
</Typography>
</Grid>
<Grid
container={true}
item={true}
direction='column'
wrap='nowrap'
>
<Grid
item={true}
container={true}
alignItems='center'
>
<Grid
item={true}
>
<TextField
label='title'
value={textInput}
onChange={(e) => {
setTextInput(e.target.value);
}}
/>
</Grid>
<Grid
item={true}
>
<Button
variant='outlined'
onClick={
() => {
addTodo(
userId,
TodoFactory({
title: textInput,
}),
);
setTextInput('');
}
}
>
Add Todo
</Button>
</Grid>
</Grid>
{
todosForUser.map((todo, index) => {
return <Grid
key={index}
item={true}
>
Here is the Checkbox
<Checkbox>
</Checkbox>
Here is the target todo list
{todo.get('title')}
</Grid>;
})
}
</Grid>
</Grid>
);
}
I'm using material-UI, typescript, react, redux, saga, immutablejs.
Basically, it's a todo list and I'm struggling with how to do a line-through on a list item when the is toggled.
Here is how it looks
When checked
When unchecked
I couldn't get my head around how to use Checkbox with onClick with this stack. Help is greatly appreciated.
Thank you.
When I was trying to mark items as deleted (a strikethrough effect) I used the <del> tag in my list.
Example:
<ListItem key={"occasion-list-item-" + occasion.Id} button onClick={() => handleToggle(occasion)}>
{ occasion.Enabled ?
(
<ListItemText id={occasion.Id} primary={JSON.stringify(occasion.Enabled)} />
)
:
(
<del>
<ListItemText id={occasion.Id} primary={JSON.stringify(occasion.Enabled)} />
</del>
)
}
</ListItem>
Where occasion is a POJO like:
{
"Id" : <some sort of string guid>,
"Enabled" : true
}
You'd have a handler which would go and update the value Enabled in that item in your component state and then react will re-render your page. When it re-renders the page, Enabled is true so chooses the first code path.

Resources