Multilevel drop downs with checkboxes in React - javascript

I want to create nested dropdowns with each having checkboxes in them like this:
Is there a way to do this in react. Couldn't find a way to implement this with any specific library

You can create a nested dropdown with checkboxes with material ui
Here's the link you can view:
https://mui.com/components/checkboxes/
And here's the source code from material UI to achieve the result:
import * as React from 'react';
import Box from '#mui/material/Box';
import Checkbox from '#mui/material/Checkbox';
import FormControlLabel from '#mui/material/FormControlLabel';
export default function IndeterminateCheckbox() {
const [checked, setChecked] = React.useState([true, false]);
const handleChange1 = (event) => {
setChecked([event.target.checked, event.target.checked]);
};
const handleChange2 = (event) => {
setChecked([event.target.checked, checked[1]]);
};
const handleChange3 = (event) => {
setChecked([checked[0], event.target.checked]);
};
const children = (
<Box sx={{ display: 'flex', flexDirection: 'column', ml: 3 }}>
<FormControlLabel
label="Child 1"
control={<Checkbox checked={checked[0]} onChange={handleChange2} />}
/>
<FormControlLabel
label="Child 2"
control={<Checkbox checked={checked[1]} onChange={handleChange3} />}
/>
</Box>
);
return (
<div>
<FormControlLabel
label="Parent"
control={
<Checkbox
checked={checked[0] && checked[1]}
indeterminate={checked[0] !== checked[1]}
onChange={handleChange1}
/>
}
/>
{children}
</div>
);
}
Here's the codesandbox link where I have combined the material UI component with the checkboxes.
https://codesandbox.io/s/indeterminatecheckbox-material-demo-forked-o0vvw?file=/demo.js:0-2222

Related

Material UI 5 Indeterminate checkbox doesn't work properly

I'm trying to add more children to the parent checkbox in the FormControlLabel.
Everything is working properly, however, the third child doesn't work properly. I want it to work as the Material UI Example
Here is my code
import * as React from "react";
import Box from "#mui/material/Box";
import Checkbox from "#mui/material/Checkbox";
import FormControlLabel from "#mui/material/FormControlLabel";
export default function IndeterminateCheckbox() {
const [checked, setChecked] = React.useState([true, false, false]);
const handleChange1 = (event) => {
setChecked([
event.target.checked,
event.target.checked,
event.target.checked
]);
};
const handleChange2 = (event) => {
setChecked([event.target.checked, checked[1], checked[2]]);
console.log(checked, "first");
};
const handleChange3 = (event) => {
setChecked([checked[0], event.target.checked, checked[2]]);
console.log(checked, "second");
};
const handleChange4 = (event) => {
setChecked([checked[0], event.target.checked, checked[1]]);
console.log(checked, "third");
};
const children = (
<Box sx={{ display: "flex", flexDirection: "column", ml: 3 }}>
<FormControlLabel
label="Child 1"
control={<Checkbox checked={checked[0]} onChange={handleChange2} />}
/>
<FormControlLabel
label="Child 2"
control={<Checkbox checked={checked[1]} onChange={handleChange3} />}
/>
<FormControlLabel
label="Child 3"
control={<Checkbox checked={checked[2]} onChange={handleChange4} />}
/>
</Box>
);
return (
<div>
<FormControlLabel
label="Parent"
control={
<Checkbox
checked={checked[0] && checked[1] && checked[2]}
indeterminate={checked[0] !== checked[1] && checked[2]}
onChange={handleChange1}
/>
}
/>
{children}
</div>
);
}
Any advice would be greatly appreciated, I am new to Material UI and react so please excuse any syntax problems.
Thank you!

How to Style label on MUI Radio Button when checked

Good day everyone I'm trying to style the label text on the radio button to change to a blue color when selected.
THIS IS MY CODE OF THE MUI BUTTON SO FAR
import * as React from "react";
import Radio from "#mui/material/Radio";
import RadioGroup from "#mui/material/RadioGroup";
import FormControlLabel from "#mui/material/FormControlLabel";
import FormControl from "#mui/material/FormControl";
export default function RowRadioButtonsGroup({label1, label2}) {
return (
<FormControl>
<RadioGroup
row
aria-labelledby="demo-row-radio-buttons-group-label"
name="row-radio-buttons-group"
style={{display: 'flex', gap: '2rem'}}
sx={{
'& .MuiSvgIcon-root': {
fontSize: 28,
},
}}
>
<FormControlLabel value="Sunday" control={<Radio />} label={label1}/>
<FormControlLabel value="Monday" control={<Radio />} label={label2} />
</RadioGroup>
</FormControl>
);
}
Basically just create a styled form control label and use "useRadioGroup " hook button and choose the colors for checked and unchecked
https://codesandbox.io/s/radiobuttonsgroup-demo-material-ui-forked-pix9rg?file=/demo.js
// Custom label
const StyledFormControlLabel = styled((props) => (
<FormControlLabel {...props} />
))(({ theme, checked }) => ({
".MuiFormControlLabel-label": checked && {
// Change color here
color: "red"
}
}));
// Custom FormControl
function MyFormControlLabel(props) {
// MUI UseRadio Group
const radioGroup = useRadioGroup();
let checked = false;
if (radioGroup) {
checked = radioGroup.value === props.value;
}
return <StyledFormControlLabel checked={checked} {...props} />;
}
<MyFormControlLabel value="female" control={<Radio />} label="Female" />

How to pass the search query to table filter between different JS files?

I have a datagrid table in which I'm getting my database data from an API call and I have written the table code in one file. I also have a search functionality where you can search for a particular record inside the table, but this search code is in another file. I am having difficulty of passing my state variable containing the search parameter from my search file to the table file. I have separated all my components in different pages since it'd be easier to structure them using a grid in my App.js. How do I get my search query to my table file next?
My search code:
export default function SearchInput() {
const [searchTerm, setSearchTerm] = React.useState('');
return (
<Grid item xs={3}>
<Box mt={1.6}
component="form"
sx={{
'& > :not(style)': { m: 1, width: '20ch', backgroundColor: "white", borderRadius: 1},
}}
noValidate
autoComplete="off"
>
<TextField
placeholder="Search Customer ID"
variant="outlined"
size="small"
sx={{input: {textAlign: "left"}}}
onChange={(event) => {
setSearchTerm(event.target.value);
console.log(searchTerm);
}}
/>
</Box>
</Grid>
);
}
My table code:
export default function DataTable() {
const [pageSize, setPageSize] = React.useState(10);
const [data, setData] = React.useState([]);
useEffect(async () => {
setData(await getData());
}, [])
return (
<div style={{ width: '100%' }}>
<DataGrid
rows={data}
columns={columns}
checkboxSelection={true}
autoHeight={true}
density='compact'
rowHeight='40'
headerHeight={80}
disableColumnMenu={true}
disableSelectionOnClick={true}
sx={datagridSx}
pageSize={pageSize}
onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
rowsPerPageOptions={[5, 10, 15]}
pagination
/>
</div>
);
}
App.js
function App() {
return (
<div className="App">
<Container maxWidth="false" disableGutters="true">
<Grid container spacing={0}>
<ABClogo />
<HHHlogo />
</Grid>
<Grid container spacing={0}>
<LeftButtonGroup />
<SearchInput />
<RightButtonGroup />
</Grid>
<Grid container spacing={0}>
<DataTable />
<TableFooter />
</Grid>
</Container>
</div>
);
}
Here is a minimal example using createContext(), and useReducer() to lift up state and share it between components, similar to what you are after, but as jsNoob says, there are multiple options. This is one I'm comfortable with.
The concept is explained here: https://reactjs.org/docs/context.html
Essentially you can create 'global' state at any point in your component tree and using Provider / Consumer components, share that state and functionality with child components.
//Main.js
import React, { createContext, useContext, useReducer } from 'react';
const MainContext = createContext();
export const useMainContext => {
return useContext(MainContext);
}
const mainReducer = (state, action) => {
switch(action.type){
case: 'SOMETHING':{
return({...state, something: action.data});
}
default:
return state;
}
}
export const Main = () => {
const [mainState, mainDispatch] = useReducer(mainReducer, {something: false});
const stateOfMain = { mainState, mainDispatch };
return(
<MainContext.Provider value={stateOfMain}>
<MainContext.Consumer>
{() => (
<div>
<Nothing />
<Whatever />
</div>
)}
</MainContext.Consumer>
</MainContext.Provider>
)
}
Then you can have the other components use either or both of the main state and dispatch.
//Nothing.js
import {mainContext} from './Main.js'
const Nothing = () => {
const { mainState, mainDispatch } = useMainContext();
return(
<button onClick={() => {mainDispatch({type: 'SOMETHING', data: !mainState.something})}}></button>
)
}
Clicking the button in the above file, should change the display of the below file
//Whatever.js
import {mainContext} from './Main.js'
const Whatever = () => {
const { mainState } = useMainContext();
return(
<div>{mainState.something}</div>
);
}

How to set a HTML element ID on a material-ui component?

I have a website built with Gatsby.js using the Material-UI.
Specific problem is this: I want to use the Google Tag Manager "Element Visibility" triggers. If some HTML element becomes visible, GTM should fire some GA tag.
Question is this: how can I specify the HTML ID for a material-ui component for GTM (or anything else) to find it?
First example:
// ...react imports omitted...
import makeStyles from '#material-ui/core/styles/makeStyles';
import Box from '#material-ui/core/Box';
import Grid from '#material-ui/core/Grid';
import CloseIcon from '#material-ui/icons/Close';
import Link from '~components/Link';
import ButtonSubmit from '~components/form-buttons/ButtonSubmit';
import Container from '~components/Container';
// ... all other imports are in-house code
const useStyles = makeStyles(theme => ({ /* ...styles... */}));
const GuestUserSoftSaleSecondPopup = ({ which, ...rest }) => {
const classes = useStyles();
// ...setup code omitted...
return (
<Box bgcolor="#474d5c" width="100%" py={4} className={classes.banner}>
<Container>
<Grid container direction="row" justify="space-between" alignItems="center" spacing={2}>
<Grid item xs={12} sm={1} md={3} lg={4}>
<CloseIcon onClick={handleClose} size="large" className={classes.closeIcon} />
</Grid>
<Grid item xs={12} sm={7} md={5} lg={4}>
<Link to="/subscribe" variant="h5" className={classes.linkStyle}>
Become a member for full access
</Link>
</Grid>
<Grid item xs={12} sm={4} className={classes.buttonPosition}>
<Link to="/subscribe" underline="none" className={classes.linkStyle}>
<ButtonSubmit type="button" fullWidth={false}>
See my option
</ButtonSubmit>
</Link>
</Grid>
</Grid>
</Container>
</Box>
);
};
// ...proptypes and `export` clause
Second example:
// ...react imports omitted...
import makeStyles from '#material-ui/core/styles/makeStyles';
import MuiDialog from '#material-ui/core/Dialog';
const useStyles = makeStyles(() => ({ /* ...styles... */ }));
const Dialog = ({ children, background, backdrop, isOpen, ...rest }) => {
const classes = useStyles({ background });
return (
<MuiDialog
open={isOpen}
maxWidth="sm"
fullWidth
disableBackdropClick
disableEscapeKeyDown
BackdropProps={{
className: backdrop ? classes.backdropBM : classes.backdrop
}}
PaperProps={{
className: classes.paper
}}
scroll="body"
{...rest}
>
{children}
</MuiDialog>
);
};
If you look at the API documentation for almost any of the Material-UI components, you will find at the end of the "Props" section a statement like the following example from Dialog:
Any other props supplied will be provided to the root element (Modal).
This means that any props not explicitly recognized by this component will be passed along eventually to whatever HTML element is the outermost element rendered. So for most Material-UI components, in order to add an id property, you just specify it.
My example below (a modification of the Simple Dialog demo) includes three different ids: one on the Dialog element which will be placed on the outermost div of the Modal, one specified via the PaperProps which will go on the main Paper div of the visible content of the dialog, and one on the Box wrapped around the dialog content.
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Avatar from "#material-ui/core/Avatar";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemAvatar from "#material-ui/core/ListItemAvatar";
import ListItemText from "#material-ui/core/ListItemText";
import DialogTitle from "#material-ui/core/DialogTitle";
import Dialog from "#material-ui/core/Dialog";
import PersonIcon from "#material-ui/icons/Person";
import Typography from "#material-ui/core/Typography";
import { blue } from "#material-ui/core/colors";
import Box from "#material-ui/core/Box";
const emails = ["username#gmail.com", "user02#gmail.com"];
const useStyles = makeStyles({
avatar: {
backgroundColor: blue[100],
color: blue[600]
}
});
function SimpleDialog(props) {
const classes = useStyles();
const { onClose, selectedValue, open } = props;
const handleClose = () => {
onClose(selectedValue);
};
const handleListItemClick = value => {
onClose(value);
};
return (
<Dialog
onClose={handleClose}
aria-labelledby="simple-dialog-title"
open={open}
PaperProps={{ id: "MyDialogPaperID" }}
id="ThisIDWillBeOnTheModal"
>
<DialogTitle id="simple-dialog-title">Set backup account</DialogTitle>
<Box id="MyBoxID">
<List>
{emails.map(email => (
<ListItem
button
onClick={() => handleListItemClick(email)}
key={email}
>
<ListItemAvatar>
<Avatar className={classes.avatar}>
<PersonIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={email} />
</ListItem>
))}
</List>
</Box>
</Dialog>
);
}
SimpleDialog.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
selectedValue: PropTypes.string.isRequired
};
export default function SimpleDialogDemo() {
const [open, setOpen] = React.useState(false);
const [selectedValue, setSelectedValue] = React.useState(emails[1]);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = value => {
setOpen(false);
setSelectedValue(value);
};
return (
<div>
<Typography variant="subtitle1">Selected: {selectedValue}</Typography>
<br />
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open simple dialog
</Button>
<SimpleDialog
selectedValue={selectedValue}
open={open}
onClose={handleClose}
/>
</div>
);
}
Material UI components don't let you set an id for them since the implementation inside should be a black box and may contain multiple html element. See if you can wrap the element in a div and put the id on that instead.
Another option would be to add a class (via the classes prop) to the element instead but I'm not sure if Google Tag Manager can use those instead of ids.

How to create checkbox in A React Function

I am trying to create a Checkbox to display and hide my React Table with some data in it. I have only used classes and am new to using Functions and as a result am struggling to implement a way to create a checkbox that can hide my React Table or a column.
import "react-table/react-table.css";
import React, { Component, useState, useEffect } from "react";
function dataquery() {
return fetch("www.website.com").then(response =>
response.json()
);
}
function Offence() {
const [count, setCount] = useState([]);
useEffect(() => {
dataquery().then(headlines => {
setCount(headlines);
});
}, []);
//event.preventDefault();
console.log(count.offences);
let data = [{}];
if (typeof count.offences !== "undefined" ) {
let newdata = count.offences.map(count => data.push({ name: count }));
// console.log("???");
}
console.log(typeof count.offence);
const columns = [
{
Header: "Name",
accessor: "name",
},
];
// trying to hide this react table or just hide the column
return <ReactTable data={data} columns={columns} filterable />;
}
export default Offence;
I am exporting this function into another file in which I render 'Offence'.
The fetch website is not the actual website as it needs a private login to work, so I replaced it with this placeholder for the purpose of this.
Thank you.
You can easily add an useState hook, which stores a boolean to show/not show the table. Then toggle this state when changing a checkbox. For example:
function Offence() {
// ... your component code
const [showTable, setShowTable] = useState(true);
// output
return (
<>
<input
type="checkbox"
checked={showTable}
onChange={() => { setShowTable(p => !p); }}
/>
{showTable ? <ReactTable ... /> : null}
</>
);
}
I have only used classes and am new to using Functions
In such a situation, I highly recommend to use materialui components. It would make your life much easier because you need not to step into gory details of styling anymore if you do not want to.
Below I roughly drafted a component which contains a table and a list of checkboxes for switching hide/show columns.
Hope it helps :)
import React, { useState, useEffect } from 'react'
import Grid from '#material-ui/core/Grid';
import Card from '#material-ui/core/Card';
import CardHeader from '#material-ui/core/CardHeader';
import CardContent from '#material-ui/core/CardContent';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import FormControl from '#material-ui/core/FormControl';
import FormGroup from '#material-ui/core/FormGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel'
import Checkbox from '#material-ui/core/Checkbox';
function App() {
const [showName, setshowName] = useState(true);
const [showQty, setshowQty] = useState(true);
const [showPrice, setshowPrice] = useState(true);
const dummydata = [{ name: "apple", qty: 12, price: 3.3 }, { name: "orange", qty: 3, price: 1.5 }, { name: "grape", qty: 10, price: 4.3 }]
return (
<Grid
container
direction="row"
justify="space-around"
alignItems="center"
>
<Grid item xs={12} sm={5}>
<Card>
<Table>
<TableHead>
<TableRow>
{showName ? <TableCell>Name</TableCell> : ""}
{showQty ? <TableCell >Qty.</TableCell> : ""}
{showPrice ? <TableCell >Price</TableCell> : ""}
</TableRow>
</TableHead>
<TableBody>
{dummydata.map(item => (
<TableRow>
{showName ? <TableCell component="th" scope="row" padding="none">
{item.name}
</TableCell> : ""}
{showQty ? <TableCell>{item.qty}</TableCell> : ""}
{showPrice ? <TableCell>{item.price}</TableCell> : ""}
</TableRow>
))}
</TableBody>
</Table>
</Card>
</Grid>
<Grid item xs={12} sm={5}>
<Card>
<CardHeader
title="Hide any column?"
/>
<CardContent>
<FormControl style={{ margin: 4 }}>
<FormGroup>
<FormControlLabel
control={
<Checkbox onChange={(e, checked) => setshowName(checked)} checked={showName} />
}
label="Hide Name"
/>
<FormControlLabel
control={
<Checkbox onChange={(e, checked) => setshowQty(checked)} checked={showQty} />
}
label="Hide Quantity"
/>
<FormControlLabel
control={
<Checkbox onChange={(e, checked) => setshowPrice(checked)} checked={showPrice} />
}
label="Hide Price"
/>
</FormGroup>
</FormControl>
</CardContent>
</Card>
</Grid>
</Grid>
);
}
export default App;

Categories