Reactjs tests (react testing library) - reactjs

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>
);
}

Related

React : Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement

I never see this error before.
I found where error is occurred.
However, i don't know why it occurred.
there is no return code before setState. If i use {imgPath} only, there is no error occurred.
What I do
I create another state for test. If i use state into component, error occurred. It is line 86.
this is my full code
I delete import component code
import { FC, useRef, useState } from 'react';
import styled from 'styled-components';
import { 팀정보변경, uploadLogo } from 'controller/modifyTeam';
import { getTeamInformation } from 'controller/team';
// module
import { useSnackbarStore } from 'module/store/snackbar';
import { useTeamStore } from 'module/store/team';
const Form: FC<Props> = ({ teamname, timezone, id, name, url }) => {
const { updateTeam } = useTeamStore();
const { pushSnackbar } = useSnackbarStore();
const [isEmpty, setIsEmpty] = useState<boolean>(false);
const [imgPath, setImgPath] = useState<string>('');
const [test, setTest] = useState<string>('');
// ref
const teamNameRef = useRef<HTMLInputElement>(null);
const timeLineRef = useRef<HTMLInputElement>(null);
const imgRef = useRef<HTMLInputElement>(null);
const widthSX = (width: string) =>
useSX({
width: `${width}`,
});
const paddingSX = useSX({
padding: '4px 12px !important',
});
// formData
const onClickUploadImage: () => void = () => imgRef?.current?.click();
const onDeleteImage: () => void = () => setImgPath('');
const updateTeamInformation: () => void = async () => {
const res = await getTeamInformation();
updateTeam(res.data.result);
};
const onSubmit: () => void = async () => {
const timeline = timeLineRef.current.value.slice(4, 10).replaceAll(':', '');
const res = await 팀정보변경(teamNameRef.current.value, timeline, imgPath, id);
if (res.data.msg === 'created') {
await updateTeamInformation();
pushSnackbar({
id: 'teamworthchanged',
type: 'Info',
title: `${t('snackbar.team_worth_changed')}`,
});
}
};
const onChangeImage: (event: any) => void = async (event: any) => {
const formData = new FormData();
formData.append('logo', event.target.files[0]);
const res = await uploadLogo(formData);
setImgPath(res.data.result.path);
};
return (
<>
<SideBar index={1} />
<Container>
<Contents xs={12} sm={8} md={6} lg={5}>
<Font className="h5">{t('nav.menu.settings.basic')}</Font>
<WhiteSpace />
<Grid container item xs={12} direction="column">
{/* 기본설정 */}
<Font className="body-2">{t('settings.basic.profile.heading')}</Font>
<Font className="caption">{t('settings.basic.profile.sub')}</Font>
<Grid container flexWrap="wrap" justifyContent="space-between">
<WhiteSpace />
<Grid xs={4} item container alignItems="center">
{imgPath === '' ? (
url === null ? (
<DefaultImg
container
justifyContent="center"
alignItems="center"
sx={widthSX('80px')}
>
{name?.slice(0, 1)}
</DefaultImg>
) : (
<TeamIcon src={url} alt="팀 로고" />
)
) : (
<TeamIcon src={imgPath} alt={'팀 로고'} />
)}
</Grid>
{/* 이미지 업로드 */}
<Grid container item xs="auto" alignItems="center" flexWrap="wrap" sx={UploadImageSX}>
<label htmlFor="contained-button-file">
<MuiButton
content={
<Grid container alignItems="center">
<Grid item>{t('btn.upload_image')}</Grid>
<Grid item>
<MdiIcon width={16} height={16} src="/icons/upload/ic_upload_white.svg" />
</Grid>
</Grid>
}
size="small"
type="contained"
sx={paddingSX}
onClick={onClickUploadImage}
/>
</label>
<ImageInput
accept="image/*"
id="contained-button-file"
onChange={onChangeImage}
type="file"
ref={imgRef}
/>
{/* 이미지 삭제 버튼 */}
<MuiButton
content={t('btn.remove')}
size="small"
color={palette.gray3}
sx={paddingSX}
onClick={onDeleteImage}
/>
</Grid>
</Grid>
</Grid>
<WhiteSpace />
{/* 팀이름 INPUT */}
<Input
label="common.team_name"
isEmpty={isEmpty}
setIsEmpty={setIsEmpty}
ref={teamNameRef}
value={teamname}
/>
<WhiteSpace />
{/* 시간대 */}
<Grid container item xs={12} direction="column">
<Font className="body-2">{t('settings.basic.timezone.heading')}</Font>
<Font className="caption">{t('settings.basic.timezone.sub')}</Font>
</Grid>
<WhiteSpace />
{/* <TimeLine currentTime={timezone} time={time} setTime={setTime} /> */}
<Timeline ref={timeLineRef} time={timezone} />
<WhiteSpace />
<WhiteSpace />
<MuiButton
content={t('btn.save_changes')}
sx={widthSX('100%')}
type="contained"
onClick={onSubmit}
/>
</Contents>
</Container>
</>
);
};
export default Form;

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>

Material UI: best way to set elements side by side

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>

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