ReactJS Table conditional styling based on previous value - reactjs

I have a React JS app using Material-UI Table component that looks like this:
I need to change the style of the border between the last row of the same date and the first row of another date like between Date A and Date B like this:
My code to create the table is just a normal map :
<TableCell>
<Typography style={{ fontSize: 16 }}>{new Date(row.date).toLocaleDateString().slice(0, 10)}</Typography>
</TableCell>
<TableCell><Typography noWrap={true} style={{ fontSize: 16 }}>{row.user}</Typography></TableCell>
<TableCell><Typography style={{ fontSize: 16 }}>{row.duration}h</Typography></TableCell>
<TableCell><Typography style={{ fontSize: 16 }}>{row.description}</Typography></TableCell>
<TableCell style={{ width: 35 }} align="left">
<IconButton
className={classes.iconButton}
style={{ marginRight: 3 }}
onClick={() => openWorklogModalForEdit(row.id, row.date, row.duration, row.description, row.project_id, row.user_id, row.project, row.clientName)}>
<EditIcon className={classes.actionsButtonIcon} />
</IconButton>
</TableCell>
<TableCell style={{ width: 65 }} align="right" >
<IconButton
className={classes.iconButton}
onClick={() => deleteWorklog(row.id)}>
<DeleteIcon className={classes.actionsButtonIcon} />
</IconButton>
</TableCell>
</TableRow>
Thank you for your time!

You can add class for first table row of different date when using .map. Please refer to this code snippet for a simple demo.
Thoughts:
As you're using map to construct a table from an array, the syntax is
let newArray = arr.map(callback(currentValue[, index[, array]]) {
// return element for newArray, after executing something
}[, thisArg]);
docs: link
Then in the map callback body, you have access to current value and index. Then you can check whether the current item is the first of a new 'group', or a new date. If true, you can add a custom class to this row (as the demo is doing).
Then you can use css to add the border, so that you can achieve 'conditional styling based on previous value'.

If you can change the styling by adding or deleting class on some element this can easily be done.
<td className={shouldBeBlack ? 'black' : 'orange'} ></td>
so like depending on value of shouldBeBlack you can set css property of the table data or any element. Just depends how u r styling.
If there is some prop you can pass to the material ui element then same c=kind of ternary equation can be made for the same.

By apply mapping, you can add check like this
rows.map((row,index)=>{
if(index !== 0)?
<td className={row.date === rows[index-1].date? 'blackLine' : 'orangeLine'} </td>
:
<td className='black'} ></td>
})

Related

React table v7 hover functionality

How can I add a drag icon on hover for rows in a table using react-table v7? The desired functionality is similar to the attached image. Can you provide some guidance or code examples to accomplish this?
image
Right now I have designed the rows(Here is the image of my current progressMy progress) but I am not able to find the logic how can I add the drag icon after row? Please help
<tr
{...row.getRowProps()}
ref={dropRef}
style={{ opacity, backgroundColor: 'white' }}
>
{/* {dragRow ? (
<DataTableBodyCell
ref={dragRef}
// noBorder={noEndBorder && rows.length - 1 === index}
align={'left'}
beforeColor="#6FF09C"
>
<DragIndicatorIcon
sx={{ fontSize: 80, width: '25px', height: '25px' }}
size="large"
/>
</DataTableBodyCell>
) : null} */}
{row.cells.map((cell) => (
<>
<DataTableBodyCell
noBorder={noEndBorder && data.length - 1 === index}
align={cell.column.align ? cell.column.align : 'left'}
cursor={handleRowStyle?.cursorStyle}
backgroundColor={handleRowStyle?.backgroundColor}
{...cell.getCellProps()}
beforeColor={cell?.row?.original?.isUsed?"#ffb100":"#6FF09C"}
cell={cell}
>
{cell.render('Cell')}
</DataTableBodyCell>
</>
))}
</tr>
This is how the row is rendered, I need to find out that How can I add drag icon outside the row and still I can drag and drop rows.

Each child in a list should have a unique "key" prop in table React

I don't know where is the error I'm getting in the console, everything seems fine, does anyone have any idea what could be wrong?
Unique key error
<TableBody>
{announcements.map((announcement) => (
<TableRow
key={announcement.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell align="left">
<Box display='flex' alignItems='center'>
<Avatar src={announcement.photoUrl} alt={announcement.announcementTitle}
style={{ height: 50, width: 50, marginRight: 20 }} />
<span>{announcement.announcementTitle}</span>
</Box>
</TableCell>
<TableCell align="right">
<Button onClick={() => handleSelectAnnouncement(announcement)} startIcon={<Edit />} />
<Button startIcon={<Delete />} color='error' />
</TableCell>
</TableRow>
))}
</TableBody>
After changing to key={index} I get this error, but I still don't know what is wrong. There are only 6 ID fields in the database, and it can't be duplicated anywhere.
warning after update key
Below is a link to the repository because there is quite a lot of code.
Last commit on GitHub
Most probably here you have a duplicate announcement.id values you can fix it like this use the index as key it will be always unique :
{announcements.map((announcement,index) => (
<TableRow
key={index}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }} > ... </TableRow>
You are likely passing some undefined or null value to the key prop. Check whether every announcement object has an string id property.
However, it's not recommended to use the array index as unique key when the array comes from a database (or will change at some point).

How to make the Material UI's table header sticky in relation to an outer scroll?

I have a page that uses Material UI's table and I need to get the sticky header working when the entire page is scrolled, since the table must be allowed to take as much vertical space as it needs, like in this pure HTML+CSS example.
I couldn't manage to do this with MUI's table, though. How can it be achieved?
demo
Set overflowX to initial on TableContainer
...
<TableContainer style={{ overflowX: "initial" }}>
...
Read more on this link
Related issue
Wanted mui table with sticky header & rotated to -45 degree angle
Below din't work:
Wasted 3 hours to make stickyHeader work
<Table stickyHeader ...
Below worked:
Apply head1 CSS for TableHead:
Apply rotatedContent1 CSS for TableCell's child (NOTE: child):
Full code is something like below:
const TableStyles = theme => ({
head1: {
zIndex: 3,
position: 'sticky',
top: '0px',
},
rotatedContent1: {
transform: 'rotate(270deg)',
}
})
const Component1 = ({ classes }) => {
return (
<React.Fragment>
<Table>
<TableHead className={classes.head1}>
<TableCell>
<div className={classes.rotatedContent1}> header value </div>
</TableCell>
</TableHead>
<TableBody>
<TableRow>
<TableCell> value </TableCell>
</TableRow>
</TableBody>
</Table>
</React.Fragment>
)
}
export default withStyles(TableStyles)(Component1)

Material Table React Custom Row Rendering

How to render custom nodes from data in Material Table react
( Ex: I want to show rows only with particular filed value>0)
If I understood your question correctly, you need to show a row in the table based on your condition. You can add that same condition while preparing the data.
I have attached a sandbox for rendering the same. I took a boolean value to display the row or not in the table.
Something like this.
<>
{row?.showValue && (
<TableRow
key={row.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{row['property']}
</TableCell>
</TableRow>
)}
</>;

How to deal with "Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>. " when using react-window to render table rows

I'm using react-window to create virtual tables with react-table 7 (and material UI tables).
I'm embedding FixedSizeList instead TableBody. Something like this:
<TableBody {...getTableBodyProps()}>
<FixedSizeList
height={listHeight}
itemCount={rows.length}
itemSize={rowHeight}
>
{RenderRow}
</FixedSizeList>)
</TableBody>
and RenderRow returns the TableRows. Something like this:
const RenderRow = React.useCallback( ({ index, style }) =>
{
const row = rows[index];
prepareRow(row);
const rowProps = row.getRowProps();
return (<TableRow
{...row.getRowProps({
style,
})} />);
}
Because of how react-window works, it creates a couple of divs to implement the list scrolling, dynamically embedding the needed TableRows as required, causing a react js warning to be output.
webpack-internal:///490:506 Warning: validateDOMNesting(...): <tr> cannot appear as a child of <div>
Just ignoring this warning, isn't something I want to do, as it may cause other warning not to be noticed. (nor do I want to use a release build while testing)
So is it possible to either prevent this warning from being emitted?
Or is it possible to use react-window for table rows, without getting this warning?
Update:
Trying the setting innerElementType to tbody suggestion.
This changes the inner div that FixedSizeList renders.
from:
<div style="position: relative; height: 96px; overflow: auto; will-change: transform; direction: ltr;">
<div style="height: 96px; width: 100%;">
to
<div style="position: relative; height: 96px; overflow: auto; will-change: transform; direction: ltr;">
<tbody style="height: 96px; width: 100%;">
So the are now included inside tbody.
So I guess I also need to use outerElementType to change the outer div, to deal with the div in table warning, but I can't think of anything valid that will work...
If I didn't want to include a thead I could set outerElementType to table and innerElementType to tbody
FixedSizeList accepts an innerElementType prop to let you specify the HTML tag to use instead of div. As far as I can tell from reading the code, it more or less needs to be a tag string.
You'd probably want this innerElementType to be tbody, which would mean re-working the parent elements a bit; I'm guessing you would not want to continue using TableBody.
The only workaround I found was to completely remove the semantic html table markup (th, tr, tbody, ...). When using a Material-UI component you can specify as which HTML element it should be rendered via the "component"-prop.
I passed to all table-elements the component="div" prop, which solved the issue.
<TableHead component="div">
Good to know:
For SEO and Accessibility this is not a good implementation - but since we are using virtualized lists we already sacrificed those aspects for better performance.
Full Example
Main table
<TableContainer component="section">
<TableToolbar tableInstance={tableInstance} />
<MuiTable {...tableInstance.getTableProps()} component="div">
<TableHead component="div">
{tableInstance.headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()} component="div">
{headerGroup.headers.map((column) => (
<TableCell
component="div"
{...(column.id === "selection"
? column.getHeaderProps()
: column.getHeaderProps(column.getSortByToggleProps()))}
>
{column.render("Header")}
{column.id !== "selection" ? (
<TableSortLabel
active={column.isSorted}
// react-table has a unsorted state which is not treated here
direction={column.isSortedDesc ? "desc" : "asc"}
/>
) : null}
</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody component="div" ref={tableBodyRef} style={{ width: "100%" }}>
<FixedSizeList
height={tableBodyHeight_inPx}
itemCount={tableInstance.rows.length}
itemSize={rowHeight_inPx}
width={tableBodyWidth} // tableInstance.totalColumnsWidth + 46
className={classnames("virtualized-list", "bigScrollbars")} // showScrollbars is used for css, virtualized-list for clean(er) markup only
>
{RenderRow}
</FixedSizeList>
</TableBody>
<TableFooter component="div">
{/* <TablePagination tableInstance={tableInstance} /> */}
</TableFooter>
</MuiTable>
</TableContainer>
Render Row callback
const RenderRow = React.useCallback(
({ index, style }: { index: number; style: React.CSSProperties }) => {
const row = props.tableInstance.rows[index]
props.tableInstance.prepareRow(row)
return (
<TableRow {...row.getRowProps({ style })} className="tr" component="div">
{row.cells.map((cell: Cell<TRow>) => {
return (
<TableCell {...cell.getCellProps()} className="td" component="div">
{cell.render("Cell")}
</TableCell>
)
})}
</TableRow>
)
},
[
tableInstance.prepareRow,
tableInstance.rows,
tableInstance.state.selectedRowIds,
]
)

Resources