Dynamically add a column to react table on button click - reactjs

I am trying to add a column to the react table using an add column button, but I am a little confused on how to implement this. I want to be able to add a column to the corresponding side that the button is clicked. Do I need to create a custom function and add it to my columns array or is there an easier way to implement this?
Here is the code.
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
/* eslint-disable react/jsx-props-no-spreading */
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { useTable } from 'react-table';
import data from '../Data/data.json';
import './Table.css';
const Styles = styled.div`
table {
border-spacing: 0;
border: 1px solid black;
width: 970px;
tr {
:last-child {
td {
border-bottom: 0;
height: 500px;
}
}
}
th,
td {
margin: 0;
padding: 0.5rem;
border-bottom: 1px solid black;
border-right: 1px solid black;
:last-child {
border-right: 0;
}
}
}
`;
const HarveyBall = (initialValue) => {
const [value, setValue] = useState(initialValue);
const onClick = () => {
if(value === 'Empty'){
setValue('Quarter');
}
if(value === 'Quarter'){
setValue('Half');
}
if(value === 'Half'){
setValue('Three-Quarter');
}
if(value === 'Three-Quarter'){
setValue('Full');
}
if(value === 'Full'){
setValue('Empty');
}
};
if(value === "Empty"){
return (
<div type="button" label="Empty" className="harvey none" onClick={onClick} />
);
}
if(value === "Quarter"){
return (
<div type="button" label="Quarter" className="harvey quarters quarter" onClick={onClick} />
);
}
if(value === "Half"){
return (
<div type="button" label="Half" className="harvey quarters half" onClick={onClick} />
);
}
if(value === "Three-Quarter"){
return (
<div type="button" label="Three-Quarter" className="harvey quarters three-quarters" onClick={onClick} />
);
}
if(value === "Full"){
return (
<div type="button" label="Full" className="harvey quarters full" onClick={onClick} />
);
}
return null;
};
const defaultPropGetter = () => ({});
const EditableCell = ({
value: initialValue,
row: { index },
column: { id },
updateMyData,
}) => {
const [value, setValue] = React.useState(initialValue);
const onChange = e => {
setValue(e.target.value);
};
const onBlur = () => {
updateMyData(index, id, value);
};
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return id === "strategicInitiative" || index === 5 ? <textarea value={value} onChange={onChange} onBlur={onBlur} style={{ width: '100%', focus: 'none', outline: 'none', border: 'none',resize: 'none', expand: {height: '1em', width: '50%', padding: '3px'}}}/> : HarveyBall(initialValue);
};
const defaultColumn = {
Cell: EditableCell,
};
function Table({ columns, getHeaderProps = defaultPropGetter, updateMyData, }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data,
updateMyData,
defaultColumn,
});
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => column.hideHeader === false ? null : (
<th
{...column.getHeaderProps([
{
className: column.className,
style: column.style,
},
getHeaderProps(column),
])}
>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>;
})}
</tr>
);
})}
</tbody>
</table>
);
}
export default function MatrixTable() {
const addTasks = () => {
const taskLength = [];
for(let i = 0; i < data.length; i += 1){
for(const [key] of Object.entries(data[i])){
if(key.includes("T")){
taskLength.push(key[1]);
}
}
}
const newTaskLength = (parseInt(taskLength[taskLength.length - 1], 10) + 1) ;
for(let i = 0; i < data.length; i += 1){
data[i][`T${newTaskLength}`] = "";
}
};
const taskButton = () => (
<>
<div>Tasks</div>
<button onClick={addColumns} type='button'>Add Column</button>
</>
);
const goalButton = () => (
<>
<div>Goals</div>
<button type='button'>Add Column</button>
</>
);
const columns = React.useMemo(
() => [
{
Header: () => taskButton(),
accessor: 'tasks',
style: {
width: '255px',
height: '49px',
background: '#fdf5ed',
fontSize: '16px',
color: '#f2994a',
textAlign: 'center',
lineHeight: '49px',
},
columns: [
{
hideHeader: false,
accessor: 'T1'
},
{
hideHeader: false,
accessor: 'T2'
},
{
hideHeader: false,
accessor: 'T3'
},
{
hideHeader: false,
accessor: 'T4'
},
{
hideHeader: false,
accessor: 'T5'
},
]
},
{
Header: "Strategic Inititiatives",
accessor: "strategicInitiative ",
style: {
color: '#323b3e',
width: '460px',
height: '49px',
background: '#f2f2f2',
textAlign: 'center',
lineHeight: '49px',
},
columns: [
{
hideHeader: false,
accessor: 'strategicInitiative'
}
]
},
{
Header: goalButton(),
accessor: 'goals',
style: {
color: '#56ccf2',
width: '255px',
height: '49px',
background: '#f8fcfe',
textAlign: 'center',
lineHeight: '49px',
},
columns: [
{
hideHeader: false,
accessor: 'G1'
},
{
hideHeader: false,
accessor: 'G2'
},
{
hideHeader: false,
accessor: 'G3'
},
{
hideHeader: false,
accessor: 'G4'
},
{
hideHeader: false,
accessor: 'G5'
}
]
},
],
[]
);
const addColumns = () => {
for(let i = 0; i < columns.length; i += 1) {
if(columns[i].accessor === "tasks"){
console.log(columns[i]);
columns[i].columns.push({
hideHeader: false,
accessor: 'T6'
});
}
}
};
addColumns();
const [, setData] = useState(data);
useEffect(() => {
setData(data);
}, [data]);
const updateMyData = (rowIndex, columnId, value) => {
setData(old =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
};
}
return row;
})
);
};
return (
<Styles>
<Table columns={columns} data={data} updateMyData={updateMyData}/>
</Styles>
);
}

I would imagine that modifying the array columns which stores the data which the table uses to render, would be the correct solution. Once columns is updated, your table should re-render which will then include the updated column.

Yeah, changing columns array(not by mutation, but by setting columns prop to new required array) is the best way to go.

Related

Material-UI: The key `wrapper` provided to the classes prop is not implemented in undefined

I have a project and in this project I have several interfaces and among these interfaces there is an interface to create a table and this table contains a set of data, but I got this error:
Material-UI: The key `wrapper` provided to the classes prop is not implemented in undefined.
How can i solve the problem?
This file contains the content of the table
jobsTable.js:
import { forwardRef, useRef, useEffect } from "react";
import Checkbox from "#material-ui/core/Checkbox";
import Table from "#material-ui/core/Table";
import PropTypes from "prop-types";
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 TablePagination from "#material-ui/core/TablePagination";
import TableRow from "#material-ui/core/TableRow";
import TableSortLabel from "#material-ui/core/TableSortLabel";
import {
useGlobalFilter,
usePagination,
useRowSelect,
useSortBy,
useTable,
} from "react-table";
import clsx from "clsx";
import JobsTablePaginationActions from "./JobsTablePaginationActions";
const IndeterminateCheckbox = forwardRef(({ indeterminate, ...rest }, ref) => {
const defaultRef = useRef();
const resolvedRef = ref || defaultRef;
useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<>
<Checkbox ref={resolvedRef} {...rest} />
</>
);
});
const EnhancedTable = ({ columns, data, onRowClick }) => {
const {
getTableProps,
headerGroups,
prepareRow,
page,
gotoPage,
setPageSize,
state: { pageIndex, pageSize },
} = useTable(
{
columns,
data,
autoResetPage: true,
},
useGlobalFilter,
useSortBy,
usePagination,
useRowSelect,
(hooks) => {
hooks.allColumns.push((_columns) => [
// Let's make a column for selection
{
id: "selection",
sortable: false,
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox. Pagination is a problem since this will select all
// rows even though not all rows are on the current page. The solution should
// be server side pagination. For one, the clients should not download all
// rows in most cases. The client should only download data for the current page.
// In that case, getToggleAllRowsSelectedProps works fine.
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox
{...row.getToggleRowSelectedProps()}
onClick={(ev) => ev.stopPropagation()}
/>
</div>
),
},
..._columns,
]);
}
);
const handleChangePage = (event, newPage) => {
gotoPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setPageSize(Number(event.target.value));
};
// Render the UI for your table
return (
<div
style={{
// padding: "2rem",
border: "solid 1px #e8e4e4",
}}
className="flex flex-col min-h-full sm:rounded-16 overflow-hidden"
>
<TableContainer className="flex flex-1">
<Table {...getTableProps()}>
<TableHead>
{headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<TableCell
className="whitespace-nowrap p-4 md:p-12"
{...(!column.sortable
? column.getHeaderProps()
: column.getHeaderProps(column.getSortByToggleProps()))}
>
{column.render("Header")}
{column.sortable ? (
<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>
{page.map((row, i) => {
prepareRow(row);
return (
<TableRow
{...row.getRowProps()}
onClick={(ev) => onRowClick(ev, row)}
className="truncate cursor-pointer"
>
{row.cells.map((cell) => {
return (
<TableCell
{...cell.getCellProps()}
className={clsx("p-4 md:p-12", cell.column.className)}
>
{cell.render("Cell")}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
component="div"
classes={{
root: "flex-shrink-0 border-t-1",
}}
rowsPerPageOptions={[
5,
10,
25,
{ label: "All", value: data.length + 1 },
]}
colSpan={5}
count={data.length}
rowsPerPage={pageSize}
page={pageIndex}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: false,
}}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
ActionsComponent={JobsTablePaginationActions}
/>
</div>
);
};
EnhancedTable.propTypes = {
columns: PropTypes.array.isRequired,
data: PropTypes.array.isRequired,
onRowClick: PropTypes.func,
};
export default EnhancedTable;
This file contains the header of the table
jobLists.js:
import { motion } from "framer-motion";
import FuseUtils from "#fuse/utils";
import Avatar from "#material-ui/core/Avatar";
import Icon from "#material-ui/core/Icon";
import IconButton from "#material-ui/core/IconButton";
import Typography from "#material-ui/core/Typography";
import { useMemo, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { deleteJob } from "./store/jobsSlice";
import JobsMultiSelectMenu from "./JobsMultiSelectMenu";
import JobsTable from "./JobsTable";
import { makeStyles } from "#material-ui/core/styles";
import Slide from "#material-ui/core/Slide";
import {
openEditJobDialog,
removeContact,
toggleStarredContact,
selectJobs,
} from "./store/jobsSlice";
import { useSnackbar } from "notistack";
const useStyles = makeStyles({
button1: {
backgroundColor: "none",
"&:hover": {
backgroundColor: "#43a047",
color: "#e8e4e4",
transition: "0.3s",
borderColor: "#43a047",
},
},
button2: {
backgroundColor: "none",
"&:hover": {
backgroundColor: "#e53935",
color: "#e8e4e4",
transition: "0.3s",
borderColor: "#e53935",
},
},
});
// const onRejectUser = useCallback((id) => {
// dispatch(rejectUser(id));
// }, []);
function JobsList(props) {
const dispatch = useDispatch();
const classes = useStyles();
const contacts = useSelector(selectJobs);
const searchText = useSelector(({ jobsApp }) => jobsApp.jobs.searchText);
const user = useSelector(({ jobsApp }) => jobsApp.user);
const { selectedJobsIds } = props;
const [filteredData, setFilteredData] = useState(null);
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const handleClick = () => {
enqueueSnackbar(
"User accepted",
{ variant: "success" },
{
anchorOrigin: {
vertical: "top",
horizontal: "right",
},
},
{ TransitionComponent: Slide }
);
};
const deleteJobHandleClick = () => {
enqueueSnackbar(
"Job Deleted successfully",
{ variant: "error" },
{
anchorOrigin: {
vertical: "top",
horizontal: "right",
},
},
{ TransitionComponent: Slide }
);
};
const columns = useMemo(
() => [
{
Header: "ID",
accessor: "id",
className: "font-medium",
sortable: true,
align: "left",
},
{
Header: "Job Title",
accessor: "name",
className: "font-medium",
sortable: true,
align: "center",
},
{
Header: "Description",
accessor: "description",
sortable: true,
align: "center",
},
{
id: "action",
// width: 128,
align: "right",
sortable: false,
Cell: ({ row }) => (
<div className="flex items-center">
{/* <IconButton
onClick={(ev) => {
ev.stopPropagation();
dispatch(approveUser(row.original.id));
dispatch(toggleStarredContact(row.original.id));
handleClick(ev);
dispatch(toggleStarredContact(row.original.id));
}}
style={{ color: "green", border: "none" }}
>
<Icon>edit</Icon>
</IconButton> */}
<IconButton
onClick={(ev) => {
ev.stopPropagation();
dispatch(deleteJob(row.original.id));
deleteJobHandleClick(ev);
}}
// className={classes.button2}
style={{ color: "red", border: "none" }}
>
<Icon>delete</Icon>
</IconButton>
</div>
),
},
],
[dispatch, user.starred]
);
useEffect(() => {
function getFilteredArray(entities, _searchText) {
if (_searchText.length === 0) {
return contacts;
}
return FuseUtils.filterArrayByString(contacts, _searchText);
}
if (contacts) {
setFilteredData(getFilteredArray(contacts, searchText));
}
}, [contacts, searchText]);
if (!filteredData) {
return null;
}
if (filteredData.length === 0) {
return (
<div className="flex flex-1 items-center justify-center h-full">
<Typography color="textSecondary" variant="h5">
There are no Jobs!
</Typography>
</div>
);
}
return (
<motion.div
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1, transition: { delay: 0.2 } }}
>
<JobsTable
columns={columns}
data={filteredData}
onRowClick={(ev, row) => {
if (row) {
dispatch(openEditJobDialog(row.original));
}
}}
/>
</motion.div>
);
}
export default JobsList;

unable to handle multiple checks in Nested Table of Ant Design

check this code. when i check from one table it works properly.but when i check options from multiple tables it removes the data of other tables and only show current tables checked data,forEg :in my logic part when i select multiple checkbox from one table it uncheck all other tables
import axios from "axios";
import React, { useState, useEffect } from "react";
import permissions from "../Data/PermissionAPI.json";
import modules from "../Data/ModuleAPI.json";
import { Table, Row, Col, Form, Button, Checkbox } from "antd";
const TEsting = () => {
const [form] = Form.useForm();
const dData=[];
const [data, setData] = useState([]);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const rowSelection = {
selectedRowKeys: selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRowKeys(selectedRowKeys);
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
};
const Permissions = ({ moduleid }) => {
const perm = permissions.filter((item) => item.moduleid === moduleid);
return (
<Table
style={{ width: "100%" }}
pagination={false}
rowSelection={rowSelection}
rowKey={(record) => record.id}
dataSource={perm}
columns={columns}
/>
);
};
const handleSubmit = (values) => {
console.log("Submit Pressed");
};
const columns = [
{
dataIndex: "key",
key: "key",
},
{
title: "Permission",
dataIndex: "pname",
key: "pname",
},
{
title: "Description",
dataIndex: "pdesc",
key: "pname",
},
];
const DisplayModules = modules.map((item, index) => {
const module = item;
// console.log(module);
if (module === undefined) return false;
return (
<Col xxl={12} xl={12} xs={24} key={index}>
{data}
<div
style={{
backgroundColor: "#ffe8c2",
padding: 20,
margin: "20px 20px 20px 20px",
borderRadius: "10px",
}}
title={`${module.id} - ${module.modulename}`}
>
<div className="about-project">
<Permissions moduleid={module.id} />
</div>
</div>
</Col>
);
});
useEffect(() => {
setData(dData);
console.log(data);
}, []);
return (
<div style={{ backgroundColor: "#e6e6e6" }}>
<Form
style={{ width: "100%" }}
form={form}
name="editSite"
onFinish={handleSubmit}
>
<Row gutter={25}>
{DisplayModules}
<div className="add-form-action">
<Form.Item>
<Button size="large" htmlType="submit" type="primary" raised>
Save
</Button>
</Form.Item>
</div>
</Row>
</Form>
</div>
);
};
export default TEsting;
here what i want:i want checkbox Data from multiple tables in one state without empty previous state,it would be very helpful if you help in this.
you need to decide - what data structure you want to get at the end, after user makes his choices and checks the checkboxes:
is that something like -
{
"moduleId": "1",
"permissions": [1,4],
"moduleId": "2",
"permissions": []
}
?
Try this solution
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const handleSelect = (record, selected) => {
if (selected) {
setSelectedRowKeys((ids) => [...ids, record.id]);
} else {
setSelectedRowKeys((ids) => {
const index = ids.indexOf(record.id);
return [...ids.slice(0, index), ...ids.slice(index + 1)];
});
}
};
const rowSelection = {
selectedRowKeys: selectedRowKeys,
onSelect: handleSelect,
};

How can i display data on my react table each second by adding new row per second using react and react hooks?

I am trying to get json data each second to add as a new row in my react table.
In the console it seems data is coming up each second but in the UI none of the data is popping up. Couldn't figure out why any of that table row in not being displayed. The snippet below does not work in stackoverflow please use the sandbox for running the code.
import React, { useState, useEffect } from "react";
import { useTable } from "react-table";
// import "./styles.css";
const API_DATA_RETURNED = [
{
// I tried id: "1" as well
id: "1",
name: "TEMP001",
serialNum: "Temp Sensor",
status: "Active",
},
{
id: "2",
name: "TEMP002",
serialNum: "Temp Sensor",
status: "Unknown",
},
{
id: "3",
name: "HUM003",
serialNum: "Humidity Sensor",
status: "Active",
},
{
id: "4",
name: "HUM004",
serialNum: "Humidity Sensor",
status: "Active",
},
{
id: "5",
name: "HUM005",
serialNum: "Humidity Sensor",
status: "Active",
},
];
function SensorTable({ columns, data }) {
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable({ columns, data });
// Render the UI for your table
return (
<table {...getTableProps()} style={{ border: "solid 1px blue" }}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps()}
style={{
borderBottom: "solid 3px red",
background: "aliceblue",
color: "black",
fontWeight: "bold",
}}
>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td
{...cell.getCellProps()}
style={{
padding: "10px",
border: "solid 1px gray",
background: "papayawhip",
}}
>
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
}
function SensorContainer() {
const [page, setPage] = useState(0);
const [sensors, setSensors] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
function loadData(index) {
console.log("loadData called");
let newData = sensors;
console.log(newData);
newData.push(API_DATA_RETURNED[index]);
setSensors(newData);
console.log(sensors);
setLoading(false);
}
setLoading(true);
// GET sensor list from API
const timer = window.setInterval(() => {
if (page < API_DATA_RETURNED.length) {
loadData(page);
setPage(page + 1);
}
}, 1000);
return () => window.clearInterval(timer);
}, [page, sensors]); // This is self is componentDidMount
const columns = React.useMemo(
() => [
{
Header: "ID",
accessor: "id", // accessor is the "key" in the data
},
{
Header: "Name",
accessor: "name",
},
{
Header: "Serial Number",
accessor: "serialNum",
},
{
Header: "Status",
accessor: "status",
},
],
[]
);
if (sensors.length === 0 && !loading) {
return <div>No Senors data available</div>;
}
return (
<div>
{loading && <span>Please wait we are fetching data</span>}
<SensorTable columns={columns} data={sensors} />
</div>
);
}
export default function App() {
return <SensorContainer />;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Same CODE in sandbox

React Maximum update depth exceeded - can't spot the loop

I'm a very new dev working on legacy code, so I'll apologize now. But I'm getting this error when I try to "add a rule" to an array of rules. I did try googling this, and understand I am likely looping a setState, but I can't spot it. Below is the code I believe is relevant, I am not using useEffect, and I don't think I'm calling the function directly. Any help would be appreciated, thanks.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import {
Grid,
MenuItem,
TextField,
Typography,
IconButton,
InputAdornment,
Select,
} from '#material-ui/core';
import ArrowIcon from '#material-ui/icons/ArrowDropDown'
import AddIcon from '#material-ui/icons/Add';
import DeleteIcon from '#material-ui/icons/Clear';
import DragHandle from '#material-ui/icons/DragHandle';
import {
DropTarget,
DragSource,
} from 'react-dnd';
import registry from "../registry";
import {Can} from '../utils';
let schema = registry.actions.RUN.configDef;
const styles = theme => ({
row: {
'&:nth-of-type(even)': {
backgroundColor: theme.palette.background.default,
},
borderTop: '1px solid',
borderColor: theme.palette.grey[400]
},
header: {
fontWeight: 'normal',
textAlign: 'center'
},
border: {
border: '1px solid',
borderColor: theme.palette.grey[400],
borderRadius: '4px',
padding: '8px',
overflow: 'hidden'
},
add: {
color: theme.palette.primary.main
},
clear: {
color: theme.palette.error.main
},
textField: {
margin: '8px'
},
rightJustifiedText: {
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
margin: '8px'
},
container: {
width: '100%'
},
iconContainer: {
padding: '0px 8px 0px 8px'
},
flexContainer: {
display: 'flex',
alignItems: 'center'
}
});
function CustomArrowIcon(props) {
return (<ArrowIcon {...props} style={{position: 'absolute', left: 0}}/>)
}
function RowContent({id, onRemove, children, connectDropTarget, connectDragSource, isDragging, classes, ability, accountNumber}) {
const opacity = isDragging ? 0.5 : 1;
return (<Grid className={classes.row} item xs={12} key={id}>
{connectDropTarget(
<span className={classes.flexContainer} style={{opacity}}>
{connectDragSource(
<span className={classes.iconContainer}>
<Can I={'update'} a={'serviceActionProfile'} where={'accountNumber'} equals={accountNumber} ability={ability}>
{can => (
<IconButton disabled={!can}>
<DragHandle/>
</IconButton>
)}
</Can>
</span>
)}
{children}
<span className={classes.iconContainer}>
<Can I={'update'} a={'serviceActionProfile'} where={'accountNumber'} equals={accountNumber} ability={ability}>
{can => (
<IconButton disabled={!can} onClick={onRemove}>
<DeleteIcon className={can ? classes.clear : undefined}/>
</IconButton>
)}
</Can>
</span>
</span>
)}
</Grid>);
}
const DraggableRow = DropTarget('row', {
hover: (props, monitor) => {
const monitorItem = monitor.getItem();
const dragIndex = monitorItem.index;
const hoverIndex = props.index;
if (dragIndex === hoverIndex) return null;
let offsetY = monitor.getClientOffset().y;
// When a short item is swapped with a tall item the short item will continue to hover the tall item
// This would normally cause the two to be swapped again, and again, and again...
// Dont re-swap the items until the mouse moves in the other direction
if (dragIndex < hoverIndex && offsetY <= monitorItem.offsetY) {
monitorItem.offsetY = offsetY;
return
}
if (dragIndex > hoverIndex && offsetY >= monitorItem.offsetY) {
monitorItem.offsetY = offsetY;
return
}
props.moveRow(dragIndex, hoverIndex);
monitorItem.index = hoverIndex;
monitorItem.offsetY = offsetY;
},
}, (connect) => {
return {
connectDropTarget: connect.dropTarget()
};
})(DragSource('row', {
beginDrag: (props, monitor) => {
return { index: props.index, offsetY: monitor.getClientOffset().y };
},
}, (connect, monitor) => {
return {
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging()
};
})(withStyles(styles)(RowContent)));
class Modify extends React.Component {
constructor(props) {
super(props);
let now = Date.now();
this.state = {
rules: (props.configValues.rules || []).slice().sort((a,b) => a.index-b.index).map((rule, index) => {
return {
...rule,
toIsValid: validateToFrom('', rule.to.path.trim(), props.serviceActionProfile.jsonataVersion),
fromIsValid: validateToFrom(rule.from.type, rule.from.value.trim(), props.serviceActionProfile.jsonataVersion),
id: ++now,
index
};
})
};
}
componentDidMount() {
this._notifyParent();
}
onChangeAction = (index, event) => {
let action = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].action = action;
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeToPrefix = (index, event) => {
let prefix = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].to.prefix = prefix;
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeToPath = (index, event) => {
let path = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].to.path = path;
newRules[index].toIsValid = validateToFrom('', path.trim(), this.props.serviceActionProfile.jsonataVersion);
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeFromType = (index, event) => {
let type = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].from.type = type;
newRules[index].fromIsValid = validateToFrom(type, newRules[index].from.value.trim(), this.props.serviceActionProfile.jsonataVersion);
return {
rules: newRules
};
}, this._notifyParent);
};
onChangeFromValue = (index, event) => {
let value = event.target.value;
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules[index].from.value = value;
newRules[index].fromIsValid = validateToFrom(newRules[index].from.type, value.trim(), this.props.serviceActionProfile.jsonataVersion);
return {
rules: newRules
};
}, this._notifyParent);
};
onRemoveRule = (index) => {
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules.splice(index, 1);
newRules.forEach((map, index) => {
map.index = index;
});
return {
rules: newRules
}
}, this._notifyParent);
};
addRule = () => {
this.setState(oldState => {
let newRules = oldState.rules.slice();
newRules.push({
id: Date.now(),
index: newRules.length,
action: 'Set',
to: {
prefix: 'local.',
path: ''
},
from: {
type: 'local.',
value: ''
}
});
return {
rules: newRules
};
}, this._notifyParent);
};
_notifyParent() {
let config = {
rules: this.state.rules.map(rule => {
let clone = {
...rule
};
delete clone.id;
delete clone.toIsValid;
delete clone.fromIsValid;
return clone;
})
};
const profile = {
...this.props.serviceActionProfile, // Add any additional properties that might be on the profile and used for validation like the jsonataVersion.
name: "PLACEHOLDER",
action: 'RUN',
documents: {},
configValues: config, // rules
};
const validation = validate(profile);
this.props.onChange({}, config, validation.validOverall);
}
moveRow = (from, to) => {
this.setState(oldState => {
let newRules = oldState.rules.slice();
let moving = newRules.splice(from, 1)[0];
newRules.splice(to, 0, moving);
newRules.forEach((map, index) => {
map.index = index;
});
return {
rules: newRules
}
}, this._notifyParent);
};
render() {
const { classes, ability, accountNumber } = this.props;
const textFieldMargin = 'dense';
return (
<div className={classes.border}>
<Can I={'update'} a={'serviceActionProfile'} where={'accountNumber'} equals={accountNumber} ability={ability}>
{can => (
<React.Fragment>
<Grid container
spacing={16}
direction={"column"}>
<Grid item xs={12} container alignItems={'center'}>
<Grid item xs={1}>
<span className={classes.iconContainer}>
<IconButton className={classes.add} disabled={!can} onClick={this.addRule}>
<AddIcon/>
</IconButton>
</span>
</Grid>
<Grid item xs={10}>
<Typography component={'div'} className={classes.header}>
Rules
</Typography>
</Grid>
</Grid>
{this.state.rules.slice().sort((a,b) => a.index-b.index).map(({action, to, toIsValid, from, fromIsValid, id}, index) => {
return (<DraggableRow key={id}
index={index}
onRemove={this.onRemoveRule.bind(this, index)}
moveRow={can ? this.moveRow : ()=>{}}
ability={ability}
accountNumber={accountNumber}>
<div className={classes.container}>
<span className={classes.flexContainer}>
<TextField select
className={classes.textField}
variant={'outlined'}
margin={textFieldMargin}
style={{width: '150px'}}
value={action}
fullWidth
disabled={!can}
onChange={this.onChangeAction.bind(this, index)}>
{schema.properties.rules.items.properties.action.enum.map(value => {
return (<MenuItem key={value} value={value}>{value}</MenuItem>)
})}
</TextField>
<TextField variant={'outlined'}
label={'Field'}
className={classes.textField}
margin={textFieldMargin}
value={to.path}
fullWidth
disabled={!can}
error={!toIsValid}
onChange={this.onChangeToPath.bind(this, index)}
inputProps={{style: {paddingLeft: '4px'}}}
InputProps={{
style: {width: 'calc(100% - 14px)'},
startAdornment: (
<InputAdornment>
<Select value={to.prefix}
disabled={!can}
onChange={this.onChangeToPrefix.bind(this, index)}
IconComponent={CustomArrowIcon}
SelectDisplayProps={{
style: {paddingLeft: '24px', paddingRight: '0px'}
}}>
{schema.properties.rules.items.properties.to.properties.prefix.enum.map(value => {
return (<MenuItem key={value} value={value}>{value}</MenuItem>)
})}
</Select>
</InputAdornment>
)
}}/>
</span>
{action === 'Set' ? (
<span className={classes.flexContainer}>
<Typography className={classes.rightJustifiedText} component='span' style={{width: '150px'}}>to</Typography>
<TextField variant={'outlined'}
label={'Value'}
className={classes.textField}
margin={textFieldMargin}
value={from.value}
fullWidth
disabled={!can}
error={!fromIsValid}
onChange={this.onChangeFromValue.bind(this, index)}
inputProps={{style: {paddingLeft: '4px'}}}
InputProps={{
style: {width: 'calc(100% - 14px)'},
startAdornment: (
<InputAdornment>
<Select value={from.type}
onChange={this.onChangeFromType.bind(this, index)}
IconComponent={CustomArrowIcon}
disabled={!can}
SelectDisplayProps={{
style: {paddingLeft: '24px', paddingRight: '0px'}
}}>
{schema.properties.rules.items.properties.from.properties.type.enum.map(value => {
return (<MenuItem key={value} value={value}>{value}</MenuItem>)
})}
</Select>
</InputAdornment>
)
}}/>
</span>
) : null}
</div>
</DraggableRow>)
})}
</Grid>
</React.Fragment>
)}
</Can>
</div>
);
}
}
Modify.defaultProps = {
requiredDocuments: {},
documents: {},
configValues: {
rules: []
},
serviceActionProfile: {}
};
Modify.propTypes = {
requiredDocuments: PropTypes.object,
documents: PropTypes.object,
configValues: PropTypes.object,
onChange: PropTypes.func.isRequired,
accountNumber: PropTypes.number.isRequired,
ability: PropTypes.object.isRequired,
serviceActionProfile: PropTypes.object
};
export default withStyles(styles)(Modify);

React Data-Table search component not rendering/working like it should

I am using an npm package called react-datatable for a project and I am trying to build out a way to search through the data. I found a repo where someone was able to do this; however, his react skills are above mine and I cannot seem to make his code work properly on my end. I would just like to know why it isn't rendering anything on my front end. Any advice would be helpful. Thank you.
His code:
import React from 'react';
import styled from 'styled-components';
import { storiesOf } from '#storybook/react';
import faker from 'faker';
import Button from '../shared/Button';
import DataTable from '../../../src/index';
const createUser = () => ({
id: faker.random.uuid(),
name: faker.name.findName(),
email: faker.internet.email(),
address: faker.address.streetAddress(),
bio: faker.lorem.sentence(),
image: faker.image.avatar(),
});
const createUsers = (numUsers = 5) =>
new Array(numUsers).fill(undefined).map(createUser);
const fakeUsers = createUsers(2000);
const TextField = styled.input`
height: 32px;
width: 200px;
border-radius: 3px;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border: 1px solid #e5e5e5;
padding: 0 32px 0 16px;
&:hover {
cursor: pointer;
}
`;
const ClearButton = styled(Button)`
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
height: 34px;
width: 32px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
`;
const FilterComponent = ({ filterText, onFilter, onClear }) => (
<>
<TextField id="search" type="text" placeholder="Filter By Name" aria-label="Search Input" value={filterText} onChange={onFilter} />
<ClearButton type="button" onClick={onClear}>X</ClearButton>
</>
);
const columns = [
{
name: 'Name',
selector: 'name',
sortable: true,
},
{
name: 'Email',
selector: 'email',
sortable: true,
},
{
name: 'Address',
selector: 'address',
sortable: true,
},
];
const BasicTable = () => {
const [filterText, setFilterText] = React.useState('');
const [resetPaginationToggle, setResetPaginationToggle] = React.useState(false);
const filteredItems = fakeUsers.filter(item => item.name && item.name.toLowerCase().includes(filterText.toLowerCase()));
const subHeaderComponentMemo = React.useMemo(() => {
const handleClear = () => {
if (filterText) {
setResetPaginationToggle(!resetPaginationToggle);
setFilterText('');
}
};
return <FilterComponent onFilter={e => setFilterText(e.target.value)} onClear={handleClear} filterText={filterText} />;
}, [filterText, resetPaginationToggle]);
return (
<DataTable
title="Contact List"
columns={columns}
data={filteredItems}
pagination
paginationResetDefaultPage={resetPaginationToggle} // optionally, a hook to reset pagination to page 1
subHeader
subHeaderComponent={subHeaderComponentMemo}
selectableRows
persistTableHead
/>
);
};
storiesOf('Filtering', module)
.add('Example 1', () => <BasicTable />)
My code:
import React from 'react';
import NotesListHtml from './NoteListHtml'
import DataTable from 'react-data-table-component';
const FilterComponent = ( { filterText, onClear, onFilter }) => (
<>
<input id="search" type="text" placeholder="Filter By Name" aria-label="Search Input" value={filterText} onChange={onFilter} />
<button type="button" onClick={onClear}>X</button>
</>
)
const BasicTable = (props) => {
let result = props.notes.map(function(el) {
var o = Object.assign({}, el);
o.fullName = `${props.data.first_name} ${props.data.last_name}`;
return o;
})
const ExpandableComponent = ({ data }) =>{
return(<p style={{padding:'20px'}}>{data.contentBody}</p>)
}
const columns = [
{
name: 'Organization',
selector: row => row.type[0].label || row.type,
sortable: true,
wrap:true
},
{
name: 'Title',
selector: 'title',
sortable: true,
},
{
name: 'Date',
selector: row=>row.createdAt.split('T')[0],
sortable: true,
},
{
name: 'Author',
selector: 'fullName',
sortable: true,
},
];
const customStyles = {
headCells: {
style: {
backgroundColor:'#f3f7f9',
},
},
cells: {
style: {
color:'#79838b'
},
},
};
const [filterText, setFilterText] = React.useState('');
const [resetPaginationToggle, setResetPaginationToggle] = React.useState(false);
const filteredItems = props.notes.filter(item => item.type[0].label && item.type[0].label.toLowerCase().includes(filterText.toLowerCase()))
const subHeaderComponentMemo = React.useMemo(() => {
console.log('subheader')
const handleClear = () => {
console.log('hit1')
if (filterText) {
setResetPaginationToggle(!resetPaginationToggle);
setFilterText('');
}
}
console.log('beforereturn');
return (
<>
{/* <input id="search" type="text" placeholder="Filter By Name" aria-label="Search Input" value={filterText} onChange={onFilter} /> */}
<button type="button" onClick={handleClear}>X</button>
</>
)
}, [filterText, resetPaginationToggle])
return (
<>
<DataTable
subHeaderComponent={subHeaderComponentMemo}
className='table'
columns={columns}
data={filteredItems}
pagination={true}
highlightOnHover={true}
pointerOnHover={true}
expandableRows={true}
expandOnRowClicked={true}
expandableRowsComponent={<ExpandableComponent data={result} />}
paginationResetDefaultPage={resetPaginationToggle} // optionally, a hook to reset pagination to page 1
customStyles={customStyles}
subHeader={false}
/>
</>
);
}
export default BasicTable;

Resources