I am trying to run some tests using React Testing Library/Jest to check if the edit and delete buttons that you can find in each row of the datagrid are working properly.
I am using the latest version of Material-UI‘s datagrid package. For each column, I have set flex and a minimum width so that the columns can grow as the window size grows but never be smaller than the minimum width.
I do not know the default window size that Jest uses for tests but for some reason, the last column “Actions” can’t be found by React Testing library queries. This means that you would probably have to scroll horizontally to see the last column.
I have disabled virtualization in the Datagrid component so that all rows and columns that are hidden should be visible. I have also tried setting global.innerWidth
to a big value to see if all columns would be rendered. So far, nothing has worked.
Has anyone had the same issue or know what the solution would be for this issue?
Here is an image of how the Datagrid component looks:
Datagrid component
Here is the code of the component in question:
import { useCallback, useEffect, useRef, useState, useContext } from 'react';
import { DataGrid, GridColDef } from '@mui/x-data-grid';
import { Box } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import axios, { AxiosResponse } from 'axios';
import { useNavigate } from 'react-router-dom';
import TableTooltip from '../../components/Tooltip/Tooltip';
import { AppContext } from '../../context/provider';
import { ApiResult } from '../../api/ApiResultInterface';
import { ApiService } from '../../api/ApiSevice';
import { AlertActionTypes } from '../../context/alert/alert.action.types';
import DeleteItemModal from '../DeleteItemModal/DeleteItemModal';
import { classes, Root } from './ModelsTable.styles';
type Model = {
id: string;
companyName: string;
modelName: string;
};
type Item = { company: string; id: string };
const ModelsTable = () => {
const [models, setModels] = useState<Model[]>([]);
const [open, setOpen] = useState(false);
const [item, setItem] = useState<Item>({ company: '', id: '' });
const navigate = useNavigate();
const { dispatch } = useContext(AppContext);
// Used to not update state if component is unmounted before data fetch is complete
let _isMounted = useRef(true);
const setAlert = useCallback(
(msg: string, status: number) => {
dispatch({
type: AlertActionTypes.SetAlertMessage,
payload: new ApiResult(msg, status),
});
dispatch({ type: AlertActionTypes.OpenAlertMessage, payload: true });
},
[dispatch]
);
const fetchModels = useCallback(async () => {
try {
const response: AxiosResponse<Model[]> = await ApiService.getAllModels();
// Do nothing if component has unmounted while fetching data
if (!_isMounted.current) {
return;
}
setModels(response.data);
} catch (err) {
if (axios.isAxiosError(err) && err.response) {
const status = err.response.status;
const error = err.response.data;
setAlert(error.message, status);
} else if (err instanceof Error) {
setAlert(err.message, 400);
}
}
}, [setAlert]);
const deleteModel = async (company: string, id: string) => {
try {
let deletionResponse: AxiosResponse<{
message: string;
}> = await ApiService.deleteModel(company, id);
const modelsResponse: AxiosResponse<Model[]> =
await ApiService.getAllModels();
// Do nothing if component has unmounted while fetching data
if (!_isMounted.current) {
return;
}
setModels(modelsResponse.data);
setAlert(deletionResponse.data.message, deletionResponse.status);
} catch (err) {
if (axios.isAxiosError(err) && err.response) {
const status = err.response.status;
const error = err.response.data;
setAlert(error.message, status);
} else if (err instanceof Error) {
setAlert(err.message, 400);
}
}
};
useEffect(() => {
return () => {
_isMounted.current = false;
};
}, []);
useEffect(() => {
fetchModels();
}, [fetchModels]);
// Table columns
const columns: GridColDef[] = [
{
field: 'id',
headerName: 'Id',
description: 'Id',
headerAlign: 'center',
align: 'center',
minWidth: 100,
flex: 2,
renderCell: (cellParams) => {
const value = cellParams.value ? (cellParams.value as string) : '';
return (
<TableTooltip title={value}>
<div className={classes.cellText}>{cellParams.value}</div>
</TableTooltip>
);
},
renderHeader: (headerParams) => {
return (
<TableTooltip title={headerParams.colDef.description as string}>
<div className={classes.cellText}>
{headerParams.colDef.description}
</div>
</TableTooltip>
);
},
},
{
field: 'modelName',
description: 'Model',
headerName: 'Model',
headerAlign: 'center',
align: 'center',
minWidth: 100,
flex: 2,
renderCell: (cellParams) => {
const value = cellParams.value ? (cellParams.value as string) : '';
return (
<TableTooltip title={value}>
<div className={classes.cellText}>{cellParams.value}</div>
</TableTooltip>
);
},
renderHeader: (headerParams) => {
return (
<TableTooltip title={headerParams.colDef.description as string}>
<div className={classes.cellText}>
{headerParams.colDef.description}
</div>
</TableTooltip>
);
},
},
{
field: 'companyName',
description: 'Company',
headerName: 'Company',
headerAlign: 'center',
align: 'center',
minWidth: 100,
flex: 2,
renderCell: (cellParams) => {
const value = cellParams.value ? (cellParams.value as string) : '';
return (
<TableTooltip title={value}>
<div className={classes.cellText}>{cellParams.value}</div>
</TableTooltip>
);
},
renderHeader: (headerParams) => {
return (
<TableTooltip title={headerParams.colDef.description as string}>
<div className={classes.cellText}>
{headerParams.colDef.description}
</div>
</TableTooltip>
);
},
},
{
field: 'actions',
description: 'Actions',
headerClassName: 'last-column-header',
headerName: 'Actions',
headerAlign: 'center',
align: 'center',
hideSortIcons: true,
disableColumnMenu: true,
minWidth: 100,
flex: 1,
renderCell: (cellParams) => {
// Get model's id and company from the other columns of the row
const id = cellParams.getValue(cellParams.id, 'id')?.toString() || '';
const company =
cellParams.getValue(cellParams.id, 'companyName')?.toString() || '';
return (
<div className={classes.statusCell}>
<div className={classes.statusContainer}>
<EditIcon
className={classes.editButton}
onClick={() =>
navigate(`/dashboard/update-model/${company}/${id}`)
}
/>
<DeleteOutlineIcon
className={classes.deleteButton}
color="secondary"
onClick={() => {
setItem({ company, id });
setOpen(true);
}}
/>
</div>
</div>
);
},
renderHeader: (headerParams) => {
return (
<TableTooltip title={headerParams.colDef.description as string}>
<div className={classes.cellText}>
{headerParams.colDef.description}
</div>
</TableTooltip>
);
},
},
];
return (
<Root>
<h1 className={classes.tableTitle}>Models</h1>
<Box boxShadow={3} className={classes.modelsTableContainer}>
<DataGrid
rows={models}
loading={models.length === 0}
columns={columns}
className={classes.modelsTable}
checkboxSelection={false}
/>
</Box>
<DeleteItemModal
open={open}
item={item}
setOpen={setOpen}
setItem={setItem}
deleteItem={deleteModel}
/>
</Root>
);
};
export default ModelsTable;