Creating dynamic table - reactjs

I'm trying to create a table with dynamic data so that I can reuse the table. The headers are displaying as intended but the rows are not displaying at all.
Data:
[
{
"id": "1231",
"name": "Michael",
"phone": "11223311",
"medical": "YES"
},
{
"id": "32123",
"name": "Johnson",
"phone": "3311323",
"medical": "NO"
}
]
Headers:
const headCells = [
{id: 'id'},
{id: 'name'},
{id: 'phone'},
{id: 'medical'}
What I've done:
const rows = () => {
if (data) {
data.map((row) =>
<TableRow key={row.uuid}>
<TableCell>{row}</TableCell>
</TableRow>)
}
};
return(
<TableContainer component={Paper}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{headers.map((headCell) => (
<TableCell>{headCell.id}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows}
</TableBody>
</Table>
</TableContainer>
)
With this the headers are displaying but the rows are not. Can anybody see why?

I think you are trying to render objects.
try this:
const rows = () => {
if (data) {
data.map((row) =>
<TableRow key={row.uuid}>
<TableCell>{row.name}</TableCell>
<TableCell>{row.phone}</TableCell>
<TableCell>{row.meducal}</TableCell>
</TableRow>)
}
};
or this if you dont want to hard code it:
const rows = () => {
if (data) {
data.map((row) =>
<TableRow key={row.uuid}>
{Object.keys(row).map((item, index)=>{
return <TableCell key={index}>{row[item]}</TableCell>
})
</TableRow>)
}
};

You are now calling rows(data) with table data. Also you are not creating cells with data
Here is how you can do it
const headCells = [
{dataIndex: 'id', name: 'ID' },
{dataIndex: 'name', name: 'Name' },
{dataIndex: 'phone', name: 'Phone' },
{dataIndex: 'medical', name: 'Medical' }
];
const Header = ({ cols }) => {
return cols.map((col) => (
<TableCell>{col.name}</TableCell>
));
}
const Rows = ({ data, cols }) => {
if (data) {
return data.map((row) =>
<TableRow key={row.uuid}>
{cols.map(col => <TableCell>{row[col.dataIndex]}</TableCell>)}
</TableRow>
);
}
else return [];
};
return(
<TableContainer component={Paper}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<Header cols={headCells} />
</TableRow>
</TableHead>
<TableBody>
<Rows data={data} cols={headCells} />
</TableBody>
</Table>
</TableContainer>
)

Related

React material ui with select dropdown inside a table cell

I'm trying to create a material ui table where the status column comes as a dropdown.
Here is status column is dropdown with values 'Pending,In progress,Approved'.
code:
const columns = [
{ id: 'state', label: 'State', minWidth: 170 },
{
id: 'status ',
label: 'Status',
minWidth: 170,
align: 'right',
lookup: { 0: 'Pending', 1: 'In progress',2:'Approved' }
},
];
function createData(state,status) {
return {state,status};
}
const rows = [
createData('Create New Submission', 1),
];
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id} align={column.align}>
{column.format && typeof value === 'number'
? column.format(value)
: value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
But dropdown is not working.Any help would be Appreciated.

Make entire contents of a TableCell into a link conditionally

I've made a table with a folder icon that is clickable link. To improve UX, I'd like to make the clickable area of the link bigger. I would like to make the entire contents of 3 of 4 of the cells in a row should be clickable as a link.
Here's what I mean
I can successfully make each cell contents (as in icons or text) clickable, but that still represents only a small area in each cell.
I've tried wrapping the entire cell contents in the link, but it leads to messy mapping, and still does not make the entire contents of the cell clickable.
Here's where I'm at so far
...
interface IProps extends Omit<unknown, 'children'> {
folders?: IGetAllRequestDTO<IFolderDTO> | null;
reload: boolean;
}
const RootFoldersTable = ({ folders, reload }: IProps): JSX.Element => {
const [selectedRow, setSelectedRow] = useState('');
const classes = useStyles();
const dispatch = useDispatch();
const getCorrectFormat = useCallback(cell => {
return cell instanceof Date
? format(cell as Date, "MM/dd/yyyy hh:mmaaaaa'm'")
: cell;
}, []);
const getAllFolders = () => {
dispatch(appActions.getAllFoldersRequest({}));
};
useEffect(() => {
getAllFolders();
}, [reload]);
const openDialogWithId = (folderId: string) => {
dispatch(appActions.setDialogFormFolderId(folderId));
dispatch(appActions.setDialogOpen());
};
const onDeleteFolder = (id: string) => {
dispatch(appActions.deleteFolderRequest(id));
};
const tableHeadElements = [
{ label: 'Name', key: 'name', sortable: false },
{ label: 'Last Modified', key: 'updateAt', sortable: false },
{ label: 'Actions', sortable: false }
];
const tableHeadElements = [
{ label: 'Name', key: 'name', sortable: false },
{ label: 'Last Modified', key: 'updateAt', sortable: false },
{ label: 'Actions', sortable: false }
];
return (
<div className={classes.tableContainer}>
<TableContainer className={classes.tableBodyContainer}>
<Table className={classes.table} size="small">
<TableHead>
<TableRow className={classes.tableHeadRow}>
{tableHeadElements.map(e => (
<TableCell key={e.key} align="center">
{e.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{folders?.items.map((folder: IFolderDTO, index: number) => {
const { id, name, updatedAt } = folder;
return (
<TableRow
className={classes.tableRow}
classes={{ selected: classes.selectedRow }}
selected={selectedRow === id}
onClick={() => setSelectedRow(id)}
key={index}
>
<Link to={APP_DASHBOARD_CHILD_FOLDER_CONTENTS_PATH(id)}>
<TableCell align="center">
<IconButton color="default" size={'small'}>
<FolderIcon fontSize="default" />
</IconButton>
</TableCell>
</Link>
{[name, new Date(updatedAt)].map(cell => (
<TableCell key={index} align="center">
<Link to={APP_DASHBOARD_CHILD_FOLDER_CONTENTS_PATH(id)}>
{getCorrectFormat(cell)}
</Link>
</TableCell>
))}
<FolderActionsMenu
folderId={id}
onDeleteFolder={onDeleteFolder}
openDialogWithId={openDialogWithId}
/>
</TableRow>
);
})}
</TableBody>
Thanks!
You can create a header cell data array that describes anything you need to render the TableCell:
const headerCellData = [
{
name: 'Calories',
link: '/',
},
{
name: 'Fat (g)',
link: '/?filter=fat',
align: 'right',
},
{
name: 'Carbs (g)',
link: '/?filter=carbs',
align: 'right',
},
{
name: 'Protein (g)',
align: 'right',
},
];
Then map each data item to the TableCell:
<TableHead>
<TableRow>
{headerCellData.map((c) => (
<TableCell align={c.align}>
{c.link ? <Link to={c.link}>{c.name}</Link> : c.name}
</TableCell>
))}
</TableRow>
</TableHead>
make the entire contents of the cell clickable
If you want the entire cell clickable (not just the text), you need to play around with CSS a little bit. Remove the padding of TableCell where it's unclickable and set the padding of the container that holds the link to 16px which is the padding of the TableCell we just removed:
<TableCell align={c.align} sx={{ padding: 0 }}>
<Box sx={{ padding: '16px' }}>
{c.link ? <Link to={c.link}>{c.name}</Link> : c.name}
</Box>
</TableCell>

How can i make Material UI table scroll horizontally and vertically with fixed header

**This is material UI code for the sticky header and vertical scroll I want this with horizontal scroll also. I tried to use overflowX:"auto and scroll on paper but it didn't work" I also tried to use other tables to complete this work but got confused. OverflowX on paper isn't working. **
For more information or reference please check https://material-ui.com/components/tables/
const columns = [
{ id: 'name', label: 'Name', minWidth: 170 },
{ id: 'code', label: 'ISO\u00a0Code', minWidth: 100 },
{
id: 'population',
label: 'Population',
minWidth: 170,
align: 'right',
format: (value) => value.toLocaleString('en-US'),
},
{
id: 'size',
label: 'Size\u00a0(km\u00b2)',
minWidth: 170,
align: 'right',
format: (value) => value.toLocaleString('en-US'),
},
{
id: 'density',
label: 'Density',
minWidth: 170,
align: 'right',
format: (value) => value.toFixed(2),
},
];
function createData(name, code, population, size) {
const density = population / size;
return { name, code, population, size, density };
}
const rows = [
createData('India', 'IN', 1324171354, 3287263),
createData('China', 'CN', 1403500365, 9596961),
createData('Italy', 'IT', 60483973, 301340),
createData('United States', 'US', 327167434, 9833520),
createData('Canada', 'CA', 37602103, 9984670),
createData('Australia', 'AU', 25475400, 7692024),
createData('Germany', 'DE', 83019200, 357578),
createData('Ireland', 'IE', 4857000, 70273),
createData('Mexico', 'MX', 126577691, 1972550),
createData('Japan', 'JP', 126317000, 377973),
createData('France', 'FR', 67022000, 640679),
createData('United Kingdom', 'GB', 67545757, 242495),
createData('Russia', 'RU', 146793744, 17098246),
createData('Nigeria', 'NG', 200962417, 923768),
createData('Brazil', 'BR', 210147125, 8515767),
];
const useStyles = makeStyles({
root: {
width: '100%',
},
container: {
maxHeight: 440,
},
});
export default function StickyHeadTable() {
const classes = useStyles();
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
return (
<Paper className={classes.root}>
<TableContainer className={classes.container}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id} align={column.align}>
{column.format && typeof value === 'number' ? column.format(value) : value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 100]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
);
}
After searching I found that putting maxWidth on the container will allow you to have a vertically and horizontally scrollable table.

How can I show edit button in column cell while hovering over table row in Material UI Table?

Currently My table view is like following:
Mui Table The table has a column name 'Action' which has a edit icon button.
Now I want to show (visibility) edit icon to edit each row only when user will hover over table row.
I have tried to override MUITable theme of Material Table but the following code didn't work. can anybody help me?
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
'&:last-child': {
visibility: 'hidden'
}
}
},
MuiTableRow: {
root: {
'&$hover:hover': {
'& .MUIDataTableBodyCell-root': {
'&:last-child': {
visibility: 'visible'
}
}
}
}
}
}
});
This is an example for that purpose: https://codesandbox.io/s/material-demo-hr3te?file=/demo.js.
Basically I do something:
Add new state to store a boolean variable to determine when to show/hide a component.
Add onMouseEnter and onMouseLeave to TableRow component, for hover effect.
Then set state according to the hover event above.
That's it!
You need to add one action column in your columns array like below:
const columns = [
{
name: 'id',
label: 'id',
options: {
sort: false,
viewColumns: false,
display: false,
filter: false
}
},
{
name: 'transportationBranch',
label: 'Transportation Branch',
options: {
sort: false,
viewColumns: false
}
},
{
name: 'charge',
label: 'Charge',
options: {
filter: false,
sort: false
}
},
{
name: 'tax',
label: 'Tax',
options: {
filter: false,
sort: false
}
},
{
name: '',
label: '',
options: {
filter: false,
sort: false,
viewColumns: false,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<IconButton
id={'Edit-' + tableMeta.rowIndex}
style={{display: 'none'}}
component="button"
variant="body2"
onClick={(event) => {
console.log(event);
alert(tableMeta.rowIndex);
}}
>
<EditIcon />
</IconButton>
);
}
}
}
];
add the the following option in mui datatable options:
setRowProps: (row, dataIndex, rowIndex) => {
return {
onMouseEnter: (e) => handleRowHover(e, row, rowIndex),
onMouseLeave: (e) => handleRowHoverLeave(e, row, rowIndex)
};
},
Write these two event in your component:
function handleRowHover(event, row, rowIndex) {
let control = document.getElementById('Edit-' + rowIndex);
control.style.display = 'block';
}
function handleRowHoverLeave(event, row, rowIndex) {
let control = document.getElementById('Edit-' + rowIndex);
control.style.display = 'none';
}
Note: we used id={'Edit-' + tableMeta.rowIndex} in IconButton so
that we can use getElementById in our two events.
If you can implement this properly then you will see this type ui on your browser:
I modified my original answer according to the mods comments:
I modified ShinaBR2 answer, so that it shows only a text in the current row: https://codesandbox.io/s/material-ui-table-onhover-action-zfloy?file=/demo.js
The idea is to use the row id on mouse enter (or mouse hover) and compare it with the current row, that is hovered to display the element in the row.
export default function SimpleTable() {
const classes = useStyles();
const [showActionId, setShowActionId] = useState(-1);
return (
<TableContainer component={Paper}>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.name}
onMouseEnter={() => {
setShowActionId(row.id); // set id here
}}
onMouseLeave={() => setShowActionId(-1)}
>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">
{row.id === showActionId ? "Show me" : ""} // check the id here and display the message
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
One approach for this is css in js code , we make the icon hidden by default(hiddePin in makeStyles), then change the 'hidden' to 'visible' on hover using "&:hover $clearHidden".
below code was verified . my place is not good work with CodeSandBox(network issue) , so i just show the code here .
const useStyles = makeStyles({
table: {
minWidth: 350,
},
root: {
"&:hover $clearHidden": {
visibility: "visible"
}
},
clearHidden: {},
hiddePin: {
visibility: "hidden"
}
});
return (
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>name</TableCell>
<TableCell align="center">desc</TableCell>
<TableCell align="center">title2</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow key={row.name} hover={true}>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="center">{row.desc}</TableCell>
<TableCell align="center" className={classes.root} >
<IconButton size="small" className={clsx(classes.hiddePin, classes.clearHidden)} onClick={(event) => handlePinClick(event,row.stamp)}>
<SvgPinOutLine />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
To only show the actions in the row your hovering over, I added the following useState objects
const [showAction, setShowAction] = React.useState(false);
const [showId, setShowId] = React.useState(0);
and then in the row object
onMouseEnter={() => {
setShowAction(true);
setShowId(row.id);}
}
onMouseLeave={() => setShowAction(false)}
and then in the specific cell that shows the actions:
{row.id == showId && showAction ? (
<IconButton>
<DeleteIcon/>
</IconButton>
) : (
<Box></Box>)}
I hope this helps someone!

Creating generic table

I'm creating a generic and dynamic table where I have columns, rows and an action button at the end of an row. All data is displaying as intended but the header is not displaying over the action button.
Is there a way to display the header underline over the action button?
Data:
[
{
"id": "1231",
"name": "Michael",
"phone": "11223311",
"medical": "YES"
},
{
"id": "32123",
"name": "Johnson",
"phone": "3311323",
"medical": "NO"
}
]
const headCells = [
{dataIndex: 'id', name: 'ID' },
{dataIndex: 'name', name: 'Name' },
{dataIndex: 'phone', name: 'Phone' },
{dataIndex: 'medical', name: 'Medical' }
];
const Header = ({ cols }) => {
return cols.map((col) => (
<TableCell>{col.name}</TableCell>
));
}
const Rows = ({ data, cols }) => {
if (data) {
return data.map((row) =>
<TableRow key={row.uuid}>
{cols.map(col => <TableCell>{row[col.dataIndex]}</TableCell>)}
<TableCell >
<IconButton>
<SearchIcon color="primary"/>
</IconButton>
</TableCell>
</TableRow>
);
}
else return [];
};
return(
<TableContainer component={Paper}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<Header cols={headCells} />
</TableRow>
</TableHead>
<TableBody>
<Rows data={data} cols={headCells} />
</TableBody>
</Table>
</TableContainer>
)
Why don't you add it manually?:
const Header = ({ cols }) => {
return cols.map((col) => (
<TableCell >{col.name}</TableCell>
)).concat( <TableCell className='underline-header'>Actions</TableCell>);
}
CSS:
.underline-header {
text-decoration: underline;
}
This is happening because you are maping over your data columns, but you do not have a column for "actions". You could do what #Enchew says above or you could add another data column
const headCells = [
{dataIndex: 'id', name: 'ID' },
{dataIndex: 'name', name: 'Name' },
{dataIndex: 'phone', name: 'Phone' },
{dataIndex: 'medical', name: 'Medical' },
{
Component: () => (
<IconButton>
<SearchIcon color="primary"/>
</IconButton>
),
name: 'Actions'
}
];
const Header = ({ cols }) => {
return cols.map((col) => (
<TableCell>{col.name}</TableCell>
));
}
const Rows = ({ data, cols }) => {
if (data) {
return data.map((row) =>
<TableRow key={row.uuid}>
{cols.map(col => (
<TableCell>
{
col.Component ?
<Component /> :
<>row[col.dataIndex]</>
}
</TableCell>
)}
</TableRow>
);
}
else return [];
};
return(
<TableContainer component={Paper}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
<Header cols={headCells} />
</TableRow>
</TableHead>
<TableBody>
<Rows data={data} cols={headCells} />
</TableBody>
</Table>
</TableContainer>
)
Just a different way to approach the problem 👍🏻

Resources