Related
Code runs and I get the table but I'm also retreiving some database data. I threw a couple console.logs in the there and my query runs and returns data. However, it doesn't show up in the collapsible table. Appreciate any advice!
import React, { useContext, useState } from "react";
import PropTypes from 'prop-types';
import { useQuery, useMutation } from "#apollo/react-hooks";
import { makeStyles } from '#material-ui/core/styles';
import Box from '#material-ui/core/Box';
import Collapse from '#material-ui/core/Collapse';
import IconButton from '#material-ui/core/IconButton';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Typography from '#material-ui/core/Typography';
import Paper from '#material-ui/core/Paper';
import KeyboardArrowDownIcon from '#material-ui/icons/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#material-ui/icons/KeyboardArrowUp';
import CreateTicket from "views/Dashboard/CreateTicket";
import {
LIST_CREATIVE_FORM,
SUBMIT_CREATIVE_FORM,
COMPLETE_CREATIVE_FORM
} from "queries/formSubmission";
import { READ_ME } from "queries/users";
import { Context } from "redux/store";
export default function Tickets() {
const [state, dispatch] = useContext(Context);
const [selectedCreative, setSelectedCreative] = useState(null);
const customer_id = state.customers?.selected?.id;
const { data: me } = useQuery(READ_ME);
let { loading, data, refetch } = useQuery(LIST_CREATIVE_FORM, {
skip: !customer_id,
variables: { customerId: customer_id }
});
const [completeCreativeForm, { loading: completing }] = useMutation(COMPLETE_CREATIVE_FORM, {
skip: !customer_id,
onCompleted: () => {
refetch();
}
});
data = data?.listCreativeForm || [];
console.log(data);
const editable = me?.readMe?.user_level === "master" || me?.readMe?.user_level === "master";
const columns = [
{
Header: "Creative or Promotion Name",
accessor: "creative_promotion_name"
},
{
Header: "Creative Messaging",
accessor: "creative_message"
},
{
Header: "Start Date",
accessor: "creative_start"
},
{
Header: "End Date",
accessor: "creative_end"
},
{
Header: "Landing Page",
accessor: "landing_page"
},
{
Header: "Notes",
accessor: "notes"
},
{
Header: "BanerAds",
accessor: "bannerads",
Cell: ({ original }) => original?.bannerads ? "Yes" : "No"
},
{
Header: "SocialAds",
accessor: "socialads",
Cell: ({ original }) => original?.socialads ? "Yes" : "No"
},
{
Header: "OnlineVideo",
accessor: "onlinevideo",
Cell: ({ original }) => original?.onlinevideo ? "Yes" : "No"
},
{
Header: "Out of Home",
accessor: "out_of_home",
Cell: ({ original }) => original?.out_of_home ? "Yes" : "No"
},
{
Header: "Link to Asset",
accessor: "link_to_assets",
Cell: ({ original }) => (
<a href={original?.link_to_assets} target="_blank">
{original?.link_to_assets ? "View" : ""}
</a>
)
},
{
Header: "Submitted By",
accessor: "submitted_by",
Cell: ({ original }) => (
<div>{original?.user_submitted ? `${original.user_submitted?.first_name} ${original.user_submitted?.last_name}` : ""}</div>
)
},
{
Header: "Completed",
accessor: "completed",
Cell: ({ original }) => original?.completed
? <div style={{ color: "green" }}>Yes</div>
: <div style={{ color: "red" }}>No</div>
},
{
Header: "Completed By",
accessor: "completed_by",
Cell: ({ original }) => (
<>
<div>{original?.user_completed ? `${original.user_completed?.first_name} ${original.user_completed?.last_name}` : ""}</div>
{editable && !original?.completed && (
<a
href="#"
onClick={(e) => {
e.preventDefault();
completeCreativeForm({
variables: {
id: original?.id
}
});
}}
>
Complete
</a>
)}
</>
)
},
{
Header: "",
accessor: "update",
Cell: ({ original }) => (
editable && !original?.completed && (
<a
href="#"
onClick={(e) => {
e.preventDefault();
setSelectedCreative(original);
}}
>
Update
</a>
)
)
}
];
Tickets.propTypes = {
offline: PropTypes.bool
};
const useRowStyles = makeStyles({
root: {
'& > *': {
borderBottom: 'unset',
},
},
});
function Row(props) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
console.log("data");
console.log(data);
return (
<React.Fragment>
<TableRow className={classes.root} columns={columns} data={data}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<TableCell align="right">{data.values.creative_promotion_name} lhey</TableCell>
<TableCell align="right">{data.values.creative_message}</TableCell>
<TableCell align="right">{data.values.creative_start}</TableCell>
<TableCell align="right">{data.values.creative_end}</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
History
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
{/*<TableBody>
{row.history.map((historyRow) => (
<TableRow key={historyRow.date}>
<TableCell component="th" scope="row">
{historyRow.date}
</TableCell>
<TableCell>{historyRow.customerId}</TableCell>
<TableCell align="right">{historyRow.amount}</TableCell>
<TableCell align="right">
{Math.round(historyRow.amount * row.price * 100) / 100}
</TableCell>
</TableRow>
))}
</TableBody>*/}
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
console.log(columns)
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table" columns={columns} data={data}>
<TableBody>
{data.map((row) => (
<Row key={row.id} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
Ok so here's what code worked for me to pass my database data to the collapsible table...
function Row(props) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
return (
<React.Fragment>
<TableRow>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<TableCell align="right">{row.creative_promotion_name}</TableCell>
<TableCell align="right">{row.creative_message}</TableCell>
<TableCell align="right">{row.creative_start}</TableCell>
<TableCell align="right">{row.creative_end}</TableCell>
<TableCell align="right">{row.out_of_home}</TableCell>
<TableCell align="right">{row.link_to_assets}</TableCell>
<TableCell align="right">{row.submitted_by}</TableCell>
<TableCell align="right">{row.completed}</TableCell>
<TableCell align="right">{row.completed_by}</TableCell>
<TableCell align="right">{row.update}</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={4}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box margin={1}>
<Table size="small" aria-label="purchases">
<TableRow>
<TableCell>Notes:</TableCell>
<TableCell align="left">{row.notes}</TableCell>
</TableRow>
<TableRow>
<TableCell>Created Time:</TableCell>
<TableCell align="left">{row.created_time}
</TableCell>
</TableRow>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
const classes = useRowStyles();
return (
<div>
<TableContainer component={Paper} style={{overflowY: "auto", maxWidth: "100%", maxHeight: "600px"}}>
<h2 className={classes.pageHeader}>Tickets</h2>
<Table stickyHeader aria-label="collapsible table" columns={columns} data={data}>
<TableHead>
<TableRow>
<TableCell />
{columns.map((data) => (
<TableCell>{data.Header}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{data.map((datarow) => (
<Row data={datarow.id} row={datarow} />
))}
</TableBody>
</Table>
</TableContainer>
</div>
);
}
I'm using the DenseTable component of the ui material and wanted to break a string into parts
here is my table:
I want in the cursos column, in the line that has LEIT - 1st Ano/LEE - 1st Ano I want to break the string where there is the '/'
below my DenseTable component code:
function createData(aulaID, numAula, tipoAula, nome, curso, data, acao) {
return { aulaID, numAula, tipoAula, nome, curso, data, acao };
}
export default function DenseTable() {
const { selectedRow, setSelectedRow, cursoSigla } = useListarAulas();
const [rows, setRows] = React.useState([]);
const {
showLancarSumarioModal,
handleOpenLancarSumarioModal,
handleCloseLancarSumarioModal,
} = useEscreverSumario();
const {
aulas,
// docente,
} = useFiltrarAulas();
React.useEffect(() => {
if (rows.length !== aulas.length) {
const tempRows = [];
aulas.map((aula) =>
tempRows.push(
createData(
aula.id,
aula.numero,
aula.tipo,
aula.disciplina.codigo,
cursoSigla(aula.cursos),
aula.data,
'Lançar Sumario'
)
)
);
setRows(tempRows);
}
}, [aulas, rows]);
return (
<>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Disciplina</TableCell>
<TableCell align="right">Curso</TableCell>
<TableCell align="right">Data</TableCell>
<TableCell align="right">Acção</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.nome}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.nome}
</TableCell>
<TableCell align="right">{row.curso}</TableCell>
<TableCell align="right">
{row.data}
{/* moment(row.data).format('yyyy-MM-DD') */}
</TableCell>
<TableCell align="right">
<Button
size="small"
onClick={() => {
setSelectedRow(row);
handleOpenLancarSumarioModal();
}}
>
{row.acao}
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<EscreverSumarioModal
showModal={showLancarSumarioModal}
handleCloseModal={handleCloseLancarSumarioModal}
selectedRow={selectedRow}
/>
</>
);
}
the function cursoSigla() in useEffect receives a courses object where their respective acronyms are and returns a string concatenated with the acronym of each course like this "LEIT - 1º Ano/LEE - 2º Ano"
below the function code:
function cursoSigla(cursos) {
const sigla = [];
if (cursos.length > 1) {
if (sigla.length !== cursos.length) {
cursos.map((curso) => sigla.push(`${curso.sigla} - ${curso.ano}º Ano`));
return sigla.join('/');
}
}
if (cursos.length === 1) {
sigla.push(cursos[0].sigla);
return `${sigla[0]} - ${cursos[0].ano}º Ano`;
}
console.log(sigla);
return '';
}
My question is how to introduce a line break in my table cell ?
Instead of rendering them as:
<TableCell align="right">{row.curso}</TableCell>
You could do something like this instead:
<TableCell>
{row.curso.split("/").map((curs, idx) => (
<p key={idx}>{curs}</p>
))}
</TableCell>
**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.
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!
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 👍🏻