Implementing React table expanded rows - reactjs

It's my first time to use react-table librabry. I am struggling for the previous couple of days how to implement the expansion functionality using useExpanded hook
Docs and all the examples available are not clear about that so, here is my code and hope if somone can give me a hand in that
const accountData = [
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Oriana Hospital",
accountType: "Hospital ",
city: "Sharjah",
area: "Al Khan",
street: "Al Taawun",
location: "https://goo.gl/maps/Jx6oTCbXyq9C8Mrt7",
hasBranches: true,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 1,
expand: false,
branches: [
{
branchName: "Oriana Pulman ",
branchType: "Hospital ",
branchCity: "Sharjah",
branchArea: "Sharjah",
branchStreet: "Al Khan",
branchLocation: "Al Taawun",
_id: "622e2f05cc18e17eb608541a",
},
],
_id: "622e2f04cc18e17eb6085419",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Ajman Speciality",
accountType: "Hospital ",
city: "Ajman",
area: "Ajman",
street: "Al Ittihad Road",
location: "https://goo.gl/maps/9aizJaNxT7iMDVry6",
hasBranches: false,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 0,
expand: false,
branches: [],
_id: "622e2f05cc18e17eb608541b",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Al Ittihad Medical Center",
accountType: "Clinic",
city: "Ajman",
area: "Al Swan",
street: "Ittihad ",
location: "https://goo.gl/maps/ppLNbPB9CdGWqFpy6",
hasBranches: false,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 0,
expand: false,
branches: [],
_id: "622e2ff2a2c75bcb9aaf640e",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Al Zaora Medical Center",
accountType: "Clinic",
city: "Ajman",
area: "Al Swan",
street: "Ittihad ",
location: "https://goo.gl/maps/PZr5PqFf28rpdgYJ7",
hasBranches: false,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 0,
expand: false,
branches: [],
_id: "622e2ff2a2c75bcb9aaf640f",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Amina Hospital",
accountType: "Hospital ",
city: "Ajman",
area: "Al Swan",
street: "Ittihad ",
location: "https://goo.gl/maps/Bxn7tedGQxUZFNqa8",
hasBranches: true,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 3,
expand: false,
branches: [
{
branchName: "Ibn Sina ",
branchType: "Clinic",
branchCity: "Ajman",
branchArea: "Al Swan",
branchStreet: "Ittihad",
branchLocation: "https://goo.gl/maps/M3WemaX4ehbp6rSS6",
_id: "622e2ff2a2c75bcb9aaf6411",
},
{
branchName: "Amina Al Jurf",
branchType: "Clinic",
branchCity: "Ajman",
branchArea: "Al Jurf",
branchStreet: "Sheikh Khalifa",
branchLocation: "https://goo.gl/maps/VGgU5TGSBvaUnGqm6",
_id: "622e2ff2a2c75bcb9aaf6412",
},
{
branchName: "Family Medical Center",
branchType: "Clinic",
branchCity: "Ajman",
branchArea: "Ajman",
branchStreet: "Industiral Ajman",
branchLocation: "https://goo.gl/maps/wvqTbtuRakzaYTWg7",
_id: "622e2ff2a2c75bcb9aaf6413",
},
],
_id: "622e2ff2a2c75bcb9aaf6410",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Al Garhoud Private Hospital",
accountType: "Hospital ",
city: "Dubai",
area: "Al Garhoud",
street: "Reiyadh",
location: "https://goo.gl/maps/LVpuJhMBT2MrobPT6",
hasBranches: true,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 1,
expand: false,
branches: [
{
branchName: "Al Garhoud 2",
branchType: "Hospital ",
branchCity: "Dubai",
branchArea: "Mirdif",
branchStreet: "Sheikh Mohamed Bin Zayed",
branchLocation: "https://goo.gl/maps/p2bKwpnBuLaH2yxS8",
_id: "622e2ff2a2c75bcb9aaf6415",
},
],
_id: "622e2ff2a2c75bcb9aaf6414",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Emirates Hospital",
accountType: "Hospital ",
city: "Dubai",
area: "Jumeirah",
street: "Al Wasl",
location: "https://goo.gl/maps/gqHVMFvhpnGgDCiL8",
hasBranches: true,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 2,
expand: false,
branches: [
{
branchName: "Emirates Marina",
branchType: "Hospital ",
branchCity: "Dubai",
branchArea: "Marina",
branchStreet: "Marina",
branchLocation: "https://goo.gl/maps/p2bKwpnBuLaH2yxS8",
_id: "622e2ff2a2c75bcb9aaf6417",
},
{
branchName: "Emirates Clinic",
branchType: "Clinic",
branchCity: "RAK",
branchArea: "RAK",
branchStreet: "Khalid street ",
branchLocation: "https://goo.gl/maps/NMN68z5VqB9AoUKS7",
_id: "622e2ff2a2c75bcb9aaf6418",
},
],
_id: "622e2ff2a2c75bcb9aaf6416",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Al Shams Medical Diagnsotice Center",
accountType: "Group",
city: "Sharjah",
area: "Industrial area",
street: "Shams",
location: "https://g.page/alshamsmedicalgroup?share",
hasBranches: true,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 2,
expand: false,
branches: [
{
branchName: "Al Shams Al Jadeed ",
branchType: "Clinic",
branchCity: "Sharjah",
branchArea: "Industrial Sharjah",
branchStreet: "Demuscus",
branchLocation: "https://goo.gl/maps/ZbEn1or58nYKhvSK9",
_id: "622e2ff2a2c75bcb9aaf641a",
},
{
branchName: "Al Shams Clinic",
branchType: "Clinic",
branchCity: "Sharjah",
branchArea: "Industrial 3",
branchStreet: "unknown street",
branchLocation: "https://goo.gl/maps/391Dz4B4EkxASjjm6",
_id: "622e2ff2a2c75bcb9aaf641b",
},
],
_id: "622e2ff2a2c75bcb9aaf6419",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Ibn Batuta DrugStore",
accountType: "Drugstore",
city: "Sharjah",
area: "Al Nema",
street: "Al Nema",
location: "https://goo.gl/maps/6jka7BaGCguTXTza7",
hasBranches: false,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 0,
expand: false,
branches: [],
_id: "622e2ff2a2c75bcb9aaf641c",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Tamimi Drug Store",
accountType: "Drugstore",
city: "Sharjah",
area: "Industrial area",
street: "Industrial ",
location: "https://goo.gl/maps/ybWjXf2ZTBCsi5r68",
hasBranches: false,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 0,
expand: false,
branches: [],
_id: "622e2ff2a2c75bcb9aaf641d",
},
{
company: "621bb1bda49c57b938fb9b8c",
country: "6228dac2ad9aef4e064906c3",
accountName: "Al Zaora Pharmacy",
accountType: "Pharmacy",
city: "Ajman",
area: "Al Swan",
street: "Ittihad ",
location: "https://goo.gl/maps/PZr5PqFf28rpdgYJ7",
hasBranches: false,
numberOfDoctors: null,
accountNotes: [],
numberOfBranches: 0,
expand: false,
branches: [],
_id: "622e2ff2a2c75bcb9aaf641e",
},
];
export default accountData;
Then in the table if the account hasBranches is true I want to show expanding button to show the branches only onClick
and here is table code
import {
useTable,
useGlobalFilter,
useFilters,
usePagination,
useSortBy,
useRowSelect,
useExpanded,
} from "react-table";
import ColumnFilter from "./ColumnFilter";
import accountData from "./Data";
import { Columns } from "./Columns";
import { useMemo } from "react";
import "./table.css";
import GlobalFilter from "./GlobalFilter";
import { CheckBox } from "./CheckBos";
const PaginationTable = (props) => {
const columns = useMemo(() => Columns, []);
const data = useMemo(() => accountData, []);
const defaultColumns = useMemo(() => {
return {
Filter: ColumnFilter,
};
}, []);
const onClickRow = (e) => {
const row = e.target;
console.log(row);
};
const tableInstance = useTable(
{
columns,
data,
defaultColumns,
expanded: {},
},
useFilters,
useGlobalFilter,
useSortBy,
useExpanded,
usePagination,
useRowSelect,
(hooks) => {
hooks.visibleColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
Header: ({ getToggleAllPageRowsSelectedProps }) => (
<div>
<CheckBox {...getToggleAllPageRowsSelectedProps()} />
</div>
),
Cell: ({ row }) => (
<div>
<CheckBox {...row.getToggleRowSelectedProps()} />
</div>
),
},
{
// Build our expander column
id: "expander", // Make sure it has an ID
Header: ({ getToggleAllRowsExpandedProps, isAllRowsExpanded }) => (
<span {...getToggleAllRowsExpandedProps()}>
{isAllRowsExpanded ? "👇" : "👉"}
</span>
),
Cell: ({ row, index }) =>
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
row.canExpand
? (row.canExpand,
(
<span
onClick={() => {
console.log(row);
}}
{...row.getToggleRowExpandedProps({
style: {
// We can even use the row.depth property
// and paddingLeft to indicate the depth
// of the row
paddingLeft: `${row.depth * 2}rem`,
},
})}
>
{row.isExpanded ? "👇" : "👉"}
</span>
))
: null,
},
...columns,
]);
}
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
rows,
getRowId,
rowsById,
getSubRows,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
prepareRow,
gotoPage,
pageCount,
setPageSize,
canExpand,
selectedFlatRows,
allColumns,
getToggleHideAllColumnsProps,
state,
setGlobalFilter,
} = tableInstance;
const { globalFilter, pageIndex, pageSize, expanded } = state;
const expandedRows = useMemo(() => {
if (accountData?.hasBranches) {
let arr = [{ 0: false }];
let d = accountData.hasBranches;
if (d.getGroupedSamplingStationBySystemId.length > 0) {
arr = d.getGroupedSamplingStationBySystemId.map((sid, ind) => {
return { [ind]: true };
});
}
return arr;
}
}, []);
// console.log(canExpand);
return (
<>
<div className="hiding">
<div className="toggleVisibility">
<CheckBox {...getToggleHideAllColumnsProps()} /> Toggle All
</div>
{allColumns.map((column) => (
<div key={column.id}>
<label>
<input type="checkbox" {...column.getToggleHiddenProps()} />
{column.Header}
</label>
</div>
))}
</div>
<GlobalFilter filter={globalFilter} setFilter={setGlobalFilter} />
<table {...getTableProps()} className="table">
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render("Header")}
<div>{column.canFilter ? column.render("Filter") : null}</div>
<span>
{column.isSorted
? column.isSortedDesc
? " 🔽"
: " 🔼"
: ""}
</span>
</th>
))}
</tr>
))}
<tr></tr>
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<pre>
<code>
{JSON.stringify(
{ selectedFlatRows: selectedFlatRows.map((row) => row.original) },
null,
2
)}
,
</code>
</pre>
<pre>
<code>{JSON.stringify({ expanded: expanded }, null, 2)}</code>
</pre>
<div className="pagination">
<span className="pages">
Page{" "}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span className="pages">
| Go to Page{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const pageNumber = e.target.value
? Number(e.target.value) - 1
: 0;
gotoPage(pageNumber);
}}
className="pageNumber"
style={{ width: "50px" }}
/>
</span>
<select
value={pageSize}
onChange={(e) => setPageSize(Number(e.target.value))}
className="paginationDropDown"
>
{[5, 10, 25, 50].map((pageSize) => {
return (
<option key={pageSize} value={pageSize}>
Show {pageSize} per page
</option>
);
})}
</select>
<button
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
className="paginationButton"
>
{"<<"}
</button>
<button
onClick={() => previousPage()}
disabled={!canPreviousPage}
className="paginationButton"
>
Previous
</button>
<button
onClick={() => nextPage()}
disabled={!canNextPage}
className="paginationButton"
>
Next
</button>
<button
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
className="paginationButton"
>
{">>"}
</button>
</div>
</>
);
};
export default PaginationTable;
and here is the screen I am getting
and here is row log
> allCells: (9) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] canExpand:
> false cells: (9) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}] depth:
> 0 getRowProps: Æ’ (userProps) getToggleRowExpandedProps: Æ’ (userProps)
> getToggleRowSelectedProps: Æ’ (userProps) id: "12" index: 12
> isExpanded: undefined isSelected: false isSomeSelected: false
> original: {company: '621bb1bda49c57b938fb9b8c', country:
> '6228dac2ad9aef4e064906c3', accountName: 'Prime Medical Center',
> accountType: 'Group', city: 'Dubai', …} originalSubRows: [] subRows:
> [] toggleRowExpanded: Æ’ (set) toggleRowSelected: Æ’ (set) values:
> {accountName: 'Prime Medical Center', accountType: 'Group', city:
> 'Dubai', area: 'Qusais', street: 'Industrial ', …} [[Prototype]]:
> Object
OK, then I am sure it gonna be something very simple to be added
what I need to know is
How to detect expanded rows
Where to put the subrows code in the table
How to show them conditionally
I will be grateful if I can get any help soon
Thanks in advance

For anyone might come here I finally found the solution in this link https://www.freakyjolly.com/react-table-tutorial/ and here also my code
import accountData from "./Data";
export const modifiedData = accountData.map((d) => {
return {
accountName: d.accountName,
accountType: d.accountType,
city: d.city,
area: d.area,
street: d.street,
location: d.location,
numberOfBranches: d.numberOfBranches,
subRows: !d.hasBranches
? null
: d.branches.map((b) => {
return {
accountName: b.branchName,
accountType: b.branchType,
city: b.branchCity,
area: b.branchArea,
street: b.branchStreet,
location: b.branchLocation,
numberOfBranches: 0,
};
}),
};
});

Related

merging the rows and displaying it in mantine table in react

import { Table } from "#mantine/core";
import React from "react";
interface TableProps {
columns: ColumnProps[];
data: {
[key: string]: string | number;
}[];
group: string[];
}
interface ColumnProps {
key: string;
label: string;
actions?: React.ReactNode;
}
export const RowGrouping = ({ columns, data, group }: TableProps) => {
columns = [
{
key: "state_name",
label: "State",
},
{
key: "district",
label: "District",
},
{
key: "gender",
label: "Gender",
},
];
data = [
{
state_name: "Karnataka",
district: "Banglore",
gender: "Boys",
},
{
state_name: "Karnataka",
district: "Banglore",
gender: "girls",
},
{
state_name: "Telangana",
district: "Hyderabad",
gender: "Boys",
},
{
state_name: "Karnataka",
district: "Mysore",
gender: "Girls",
},
{
state_name: "Karnataka",
district: "Mysore",
gender: "Boys",
},
{
state_name: "Karnataka",
district: "Manglore",
gender: "Transgender",
},
{
state_name: "Goa",
district: "North",
gender: "Girls",
},
{
state_name: "Andhra Pradesh",
district: "Chittor",
gender: "Boys",
},
{
state_name: "Andhra Pradesh",
district: "Chittor",
gender: "Girls",
},
{
state_name: "Goa",
district: "South",
gender: "Transgender",
},
{
state_name: "Andhra Pradesh",
district: "Vizag",
gender: "Boys",
},
];
group = ["state_name", "district"];
const mergeData = data.reduce((a, b) => {
const similar = a.find((e) => e.state_name == b.state_name);
return similar ? similar.district.push(b.district) : a.push({ ...b, district: [b.district] }), a;
}, []);
const tableHead = () => {
return (
<tr>
{columns.map((c, index) => (
<th key={index}>{c.label}</th>
))}
</tr>
);
};
const tableData = () => {
return (
<>
{mergeData.map((c, index) => (
<tr key={index}>
<td>{c.state_name}</td>
{c.district.map((d, i) => (
<tr key={i}>
<tr>
<td>{d}</td>
</tr>
</tr>
))}
</tr>
))}
</>
);
};
return (
<div>
<Table withBorder withColumnBorders style={{ width: "50%", margin: "0 auto", marginTop: "50px" }}>
<thead>{tableHead()}</thead>
<tbody>{tableData()}</tbody>
</Table>
</div>
);
};
Here in this example, what I'm trying to do is that I have three columns - state, district and gender, and I'm trying to group the rows and display it in table. but I'm able to group only one column that is state but I'm unable to group district wise, and I don't know how to generalize this without hardcode means data columns should be grouped for any data by column name and displayed in table.[ this is the output I'm getting but i want the districts to be displayed properly means inside karnataka rowspan there should be only banglore, mysore and manglore and inside banglore rowspan there should be boys and girls and same for mysore and chittor as well without hardcoded. please help...
You could adjust your merge fn so that it only pushes the district if it's not already in the district array.
const mergeData = data.reduce((a, b) => {
const similar = a.find((e) => e.state_name == b.state_name);
return (
similar
? !similar.district.includes(b.district) &&
similar.district.push(b.district)
: a.push({
...b,
district: [b.district],
}),
a
);
}, []);
Output :
[
{
district: ["Banglore", "Mysore", "Manglore"],
gender: "Boys",
state_name: "Karnataka",
},
{
district: ["Hyderabad"],
gender: "Boys",
state_name: "Telangana",
},
{
district: ["North", "South"],
gender: "Girls",
state_name: "Goa",
},
{
district: ["Chittor", "Vizag"],
gender: "Boys",
state_name: "Andhra Pradesh",
},
];
Fiddle : https://jsfiddle.net/RyanZee/bnh6u7mx/4/
As for the gender being displayed, you're not returning it in your map:
{
mergeData.map((c, index) => (
<tr key={index}>
<td>{c.state_name}</td>
{c.district.map((d, i) => (
<tr key={i}>
<tr>
<td>{d}</td>
</tr>
</tr>
))}
<td>{c.gender}</td>
</tr>
));
}

With react DnD, why my state resets when every time I drop an element?

I am working on a form builder. The basic implementation works but every time when I move elements from the tool box to the form the current state resets to initial then add newly dropped element so previously dragged element is missed from the form. Below is the linked to the source code at sandbox.
https://codesandbox.io/s/nifty-https-qx8lko
index.js
import Example from "./Example.jsx";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import "./style.sass";
function App() {
return (
<div className="App">
<DndProvider backend={HTML5Backend}>
<Example />
</DndProvider>
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
Example.jsx
import React, { useState } from "react";
import Form from "./Form";
import Toolbar from "./Toolbar";
const Example = (props) => {
const [Items, setItems] = useState([{ name: "Test" }]);
const addItem = (item) => {
setItems([...Items, item]);
};
return (
<div className="card" style={{ border: "1px dashed rgb(219 163 163)" }}>
<div className="card-header d-flex justify-content-between">
<h5 className="cart-title m-0">View</h5>
</div>
<div className="card-body">
<div className="row">
<div className="col-9">
<Form items={Items} addItems={addItem} />
</div>
<div className="col-3">
<Toolbar />
</div>
</div>
</div>
</div>
);
};
export default Example;
Form.jsx
import React from "react";
import { useDrop } from "react-dnd";
import { ItemTypes } from "./ItemTypes";
function Form(props) {
const addItem = (item) => {
props.addItems(item);
};
const [{ canDrop, isOver }, drop] = useDrop(() => ({
accept: ItemTypes.CARD,
drop: (item, monitor) => addItem(item),
collect: (monitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop()
})
}));
return (
<div className="card card-default shadow-sm mt-3">
<div className="card-body">
{props.items.map((item, index) => {
return (
<div key={index} className="form-item">
{item.name}
</div>
);
})}
<div ref={drop}>
{/* {canDrop ? "Release to drop" : "Drag a box here"} */}
{isOver && canDrop && (
<div className="form-place-holder">
<div>Release to Drop</div>
</div>
)}
{!isOver && (
<div className="form-place-holder">
<div>Dropzone</div>
</div>
)}
</div>
</div>
</div>
);
}
export default Form;
Toolbar.jsx
import React from "react";
import ToolbarItem from "./ToolbarItem";
const Toolbar = () => {
const _defaultItems = () => {
return [
{
key: "Header",
name: "Header Text",
icon: "fas fa-heading",
static: true,
content: "Place holder text"
},
{
key: "Label",
name: "Label",
static: true,
icon: "fas fa-font",
content: "Place holder text"
},
{
key: "Paragraph",
name: "Paragraph",
static: true,
icon: "fas fa-paragraph",
content: "Place holder text"
},
{
key: "LineBreak",
name: "Line break",
static: true,
icon: "fas fa-arrows-alt-h"
},
{
key: "Dropdown",
canHaveAnswer: true,
name: "Dropdown",
icon: "far fa-caret-square-down",
label: "Place holder label",
field_name: "dropdown_",
options: []
},
{
key: "Tags",
canHaveAnswer: true,
name: "Tags",
icon: "fas fa-tags",
label: "Place holder label",
field_name: "tags_",
options: []
},
{
key: "Checkboxes",
canHaveAnswer: true,
name: "Checkboxes",
icon: "far fa-check-square",
label: "Place holder label",
field_name: "checkboxes_",
options: []
},
{
key: "RadioButtons",
canHaveAnswer: true,
name: "Multiple choices",
icon: "far fa-dot-circle",
label: "Place holder label",
field_name: "radiobuttons_",
options: []
},
{
key: "TextInput",
canHaveAnswer: true,
name: "Text input",
label: "Place holder label",
icon: "fas fa-font",
field_name: "text_input_"
},
{
key: "NumberInput",
canHaveAnswer: true,
name: "Number input",
label: "Place holder label",
icon: "fas fa-plus",
field_name: "number_input_"
},
{
key: "TextArea",
canHaveAnswer: true,
name: "Multi line input",
label: "Place holder label",
icon: "fas fa-text-height",
field_name: "text_area_"
},
{
key: "TwoColumnRow",
canHaveAnswer: false,
name: "Two columns row",
label: "",
icon: "fas fa-columns",
field_name: "two_col_row_"
},
{
key: "ThreeColumnRow",
canHaveAnswer: false,
name: "Three columns row",
label: "",
icon: "fas fa-columns",
field_name: "three_col_row_"
},
{
key: "FourColumnRow",
canHaveAnswer: false,
name: "Four columns row",
label: "",
icon: "fas fa-columns",
field_name: "four_col_row_"
},
{
key: "Image",
name: "Image",
label: "",
icon: "far fa-image",
field_name: "image_",
src: ""
},
{
key: "Rating",
canHaveAnswer: true,
name: "Rating",
label: "Place holder label",
icon: "fas fa-star",
field_name: "rating_"
},
{
key: "DatePicker",
canDefaultToday: true,
canReadOnly: true,
dateFormat: "MM/dd/yyyy",
timeFormat: "hh:mm aa",
showTimeSelect: false,
showTimeSelectOnly: false,
showTimeInput: false,
name: "Date",
icon: "far fa-calendar-alt",
label: "Place holder label",
field_name: "date_picker_"
},
{
key: "Signature",
canReadOnly: true,
name: "Signature",
icon: "fas fa-pen-square",
label: "Signature",
field_name: "signature_"
},
{
key: "HyperLink",
name: "Website",
icon: "fas fa-link",
static: true,
content: "Place holder website link",
href: "http://www.example.com"
},
{
key: "Download",
name: "File attachment",
icon: "fas fa-file",
static: true,
content: "Place holder file name",
field_name: "download_",
file_path: "",
_href: ""
},
{
key: "Range",
name: "Range",
icon: "fas fa-sliders-h",
label: "Place holder label",
field_name: "range_",
step: 1,
default_value: 3,
min_value: 1,
max_value: 5,
min_label: "Easy",
max_label: "Difficult"
},
{
key: "Camera",
name: "Camera",
icon: "fas fa-camera",
label: "Place holder label",
field_name: "camera_"
},
{
key: "FileUpload",
name: "File upload",
icon: "fas fa-file",
label: "Place holder label",
field_name: "file_upload_"
}
];
};
return (
<div className="react-form-builder-toolbar">
<h4>Toolbox</h4>
<ul>
{_defaultItems().map((item, index) => {
return <ToolbarItem data={item} key={index} />;
})}
</ul>
</div>
);
};
export default Toolbar;
ToolbarItem.jsx
import React from "react";
import { useDrag } from "react-dnd";
import { ItemTypes } from "./ItemTypes";
function ToolbarItem(props) {
const data = props.data;
const [{ isDragging }, drag, dragPreview] = useDrag(() => ({
type: ItemTypes.CARD,
collect: (monitor) => ({
isDragging: monitor.isDragging()
}),
item: props.data
}));
return (
<li ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }}>
<i className={data.icon}></i>
{data.name}
</li>
);
}
export default ToolbarItem;
I have tried also storing the state in redux and still same behavior. For simplicity I have kept the state in parent component in Sandbox example.
In Example.js file try replacing with this setItems((items)=>[...items, item]);
I think this will fix the issue. Check this here https://codesandbox.io/s/reactjs-dnd-example-forked-1lu5i9?file=/src/Form.jsx

How i render checkboxes with nested array of objects

I have a checkbox component which renders multiple day checkboxes. I need to print the values of the selected checkboxes. The sample given below looks like the one:
const [state, setState] = useState({
"firstName": "",
"lastName" : "",
"mobileNumber" : "",
"avalabilities": [{"availabilityId": "",
day: [
{
value: "sun",
name: "Sunday"
},
{
value: "mon",
name: "Monday"
},
{
value: "tue",
name: "Tuesday"
},
{
value: "wed",
name: "Wednesday"
},
{
value: "thur",
name: "Thursday"
},
{
value: "fri",
name: "Friday"
},
{
value: "sat",
name: "Saturday"
}
],
"isChecked": false,
"checked" : false,
"allChecked": false,
"error": null});
this is the console value given below
{firstName: '', lastName: '', mobileNumber: '', avalabilities: Array(1), …}
allChecked: false
avalabilities: Array(1)
0:
availabilityId: ""
day: (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
[[Prototype]]: Object
length: 1
[[Prototype]]: Array(0)
close: false
disabled: false
error: null
firstName: ""
isChecked: false
isPending: false
lastName: ""
mobileNumber: ""
open: false
this is how I am trying to render the arrays
{(avalabilities || [{}]).map((av, index) => {
return (
<div key={av.availabilityId}>
<>
{av.day.map((item) => {
return (
<div>
<input
checked={item.checked || false}
onChange={() => handleChange3(item.value)}
type="checkbox"
/>
</div>
);
})}
</>
But the error on mapping with day array is coming below like this
are not valid as a React child (found: object with keys {value, name}). If you meant to render a collection of children, use an array instead.
const checkedHandler = (event) => {
setState(...)
//Well, here I don't know how change the particular value of the
array...}
Any help is highly appreciated.
If i'm correct you want to show values of each checkbox and save the respective checkbox value when we toggle any checkbox.
You have avalabilities array & in each object we have another day array. I render the all the avalabilities. Now when i toggle any checkbox, i pass three things to checkHandler:
e which is native event of checkbox
avIndex which is index of object in avalabilities array
index which is index of object in day array.
Now each day object, i set a key checked and store the value of that checkbox by getting the value from e.target.checked.
Hope this solve your problem
newState.avalabilities[avIndex].day[index].checked = e.target.checked;
import { useState } from 'react';
export default function App() {
const [state, setState] = useState({
firstName: '',
lastName: '',
mobileNumber: '',
avalabilities: [
{
availabilityId: '',
day: [
{ value: 'sun', name: 'Sunday' },
{ value: 'mon', name: 'Monday' },
{ value: 'tue', name: 'Tuesday' },
{ value: 'wed', name: 'Wednesday' },
{ value: 'thur', name: 'Thursday' },
{ value: 'fri', name: 'Friday' },
{ value: 'sat', name: 'Saturday' }
],
isChecked: false,
checked: false,
allChecked: false,
error: null
}
]
});
const checkedHandler = (e, avIndex, index) => {
console.log(e.target.checked, avIndex, index);
setState((prev) => {
let newState = { ...prev };
newState.avalabilities[avIndex].day[index].checked = e.target.checked;
return newState;
});
};
return (
<>
{state.avalabilities.map((av, avIndex) => (
<div key={av.availabilityId}>
{av.day.map((item, index) => (
<div key={index}>
<input
checked={item?.checked || false}
onChange={(e) => checkedHandler(e, avIndex, index)}
type='checkbox'
/>
<span>{item.name}</span>
</div>
))}
</div>
))}
</>
);
}

Is there a way to group columns in MUI-dataTable?

I am using MUIDataTable in my React project. I just want to group columns by adding borders to the table. Please find the code below which I am using.
const columns = [
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
[customBodyRender: (value) => {
return (
<div style={{ borderRight: "solid 2px" }} >
{value}
</div>
)
}][1]
}
},
{
name: "company",
label: "Company",
options: {
filter: true,
sort: false,
}
},
{
name: "city",
label: "City",
options: {
filter: true,
sort: false,
customBodyRender: (value) => {
return (
<div style={{ borderRight: "solid 2px" }} >
{value}
</div>
)
}
}
},
{
name: "state",
label: "State",
options: {
filter: true,
sort: false,
}
},
];
const data = [
{ name: "Joe James", company: "Test Corp", city: "Yonkers", state: "NY" },
{ name: "John Walsh", company: "Test Corp", city: "Hartford", state: "CT" },
{ name: "Bob Herm", company: "Test Corp", city: "Tampa", state: "FL" },
{ name: "James Houston", company: "Test Corp", city: "Dallas", state: "TX" },
{ name: "", company: "", city: "Tampa", state: "" },
{ name: "", company: "", city: "", state: "" },
{ name: "", company: "", city: "Hartford", state: "" },
{ name: "", company: "", city: "", state: "" },
{ name: "", company: "", city: "", state: "" },
{ name: "", company: "", city: "", state: "" },
];
const options = {
filterType: 'checkbox',
};
<MUIDataTable
title={"Employee List"}
data={data}
columns={columns}
options={options}
/>
This is not working as expected. Either the border height is not proper or in case of no data in the table row, there is no border at all.
I also want the borders to appear on the table headers.
I have attached the snapshot of the output for reference.
OutputImage
Is there a way to fix this?
there is a PR but it's in beta only https://github.com/gregnb/mui-datatables/pull/1441, not yet merged into the master branch

Array state variable doesn't render

I have a table that needs to be rendered dynamically so I put in state, the problem is that I can't return it via state , it can only be queried with forEach function directly:
This code works:
render() {
let rows=[], rows2=[];
var PRODUCTS = [
{id:0,category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{id:1,category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{id:2,category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{id:3,category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{id:4,category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{id:5,category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
PRODUCTS.forEach((product)=>{
if( product.name.indexOf(this.props.searchText)==-1 && this.props.searchText!='')
{return;}
if(this.props.inStockOnly){
if(product.stocked){
if(product.category=="Sporting Goods"){
rows.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
else {
rows2.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
}
}
else {
if(product.category=="Sporting Goods"){
rows.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
else {
rows2.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
}
})
And this doesn't:
render() {
let rows=[], rows2=[];
this.state.products.forEach((product)=>{
if( product.name.indexOf(this.props.searchText)==-1 && this.props.searchText!='')
{return;}
if(this.props.inStockOnly){
if(product.stocked){
if(product.category=="Sporting Goods"){
rows.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
else {
rows2.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
}
}
else {
if(product.category=="Sporting Goods"){
rows.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
else {
rows2.push(<ProductRow product={product} key={product.id} onDestroy={this.Destroy}/>)
}
}
})
It triggers this error :
product.name.indexOf is not a function
Update
this how I set my state :
var PRODUCTS = [
{id:0,category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{id:1,category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{id:2,category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{id:3,category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{id:4,category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{id:5,category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
class ProductTable extends React.Component {
constructor(props)
{
super(props)
this.state={
products:PRODUCTS
};
}......
Can you help me ?
As #AdrianoReptti said in comments ,the problem was with name.
Actually in another component i changed the product's name in order to light out if the components is in stock or not :
product.name=product.stocked?product.name:<span style={{color:"red"}}>{product.name}</span>;
i fixed the code and now it runs :)
But it's still weird i thought that the state of a component is private and can only be changed with setState and not with the return of a children component.
For me your code is working fine. I have tried in the console. Try to restart the server.

Resources