How to add dropdown to react-table on click of icon - reactjs

I am using react table 7.0 , How can i add and iconColumn , and on click of that , need to show an dropdown.
{
Header: "",
accessor: "actionColumn",
disableSortBy: true,
Cell: ({ original }) => (
<div className="cursor__pointer ">
<Icon className="padding5" iconName="RemoveLink" aria-hidden="true" />
</div>
)
},
I am able to render an icon on the column like above. How can i render a dropdown on click of it ?

Here's an example, how to do this with this simple #trendmicro-frontend/react-dropdown library:
{
Header: "",
accessor: "actionColumn",
disableSortBy: true,
Cell: ({ original }) => (
<div className="cursor__pointer ">
<Dropdown
onSelect={(eventKey) => {
}}
>
<Dropdown.Toggle btnStyle="link" noCaret
>
<Icon className="padding5" iconName="RemoveLink" aria-hidden="true" />
</Dropdown.Toggle>
<Dropdown.Menu>
<MenuItem header>Header</MenuItem>
<MenuItem eventKey={1}>link</MenuItem>
<MenuItem divider />
<MenuItem header>Header</MenuItem>
<MenuItem eventKey={2}>link</MenuItem>
</Dropdown.Menu>
</Dropdown>
</div>
)
},
A working example here:
https://codesandbox.io/s/distracted-leftpad-c6onr?file=/src/App.js

Related

click on MUI's select in bootstrap dropdown makes it disappear

As the title said, I have a material UI's select in a bootstrap dropdown. When I click on the select, the dropdown closes, and the popover than appear is placed in a bad place.
Assuming that the popover should be created inside the dropdown for it not to happen, I tried to make it this way, using the anchorEl.
The console tells me "Popover.js:162 MUI: The anchorEl prop provided to the component is invalid.
The anchor element should be part of the document layout.
Make sure the element is present in the document or that it's not display none."
And it doesn't fix the issue. I'm quite lost, any idea? As a detail, I made a test using Button + Popover instead of the select, and it works.
Code:
import "./styles.css";
import React, { useRef, useState } from "react";
import Select from "#mui/material/Select";
import FormControl from "#mui/material/FormControl";
import MenuItem from "#mui/material/MenuItem";
function MySelect() {
const ref = useRef();
const [value, setValue] = useState("null");
const [anchor, setAnchor] = useState(null);
const handleChange = () => {};
const onOpen = (e) => {
setAnchor(e.currentTarget);
};
return (
<>
<div ref={ref}>
<FormControl fullWidth>
<Select
value={value}
onOpen={onOpen}
onChange={handleChange}
MenuProps={{ anchorEl: anchor, anchorReference: "anchorEl" }}
>
<MenuItem value="null">Hey</MenuItem>
<MenuItem value="hello">Hello</MenuItem>
<MenuItem value="hello2">Hello</MenuItem>
<MenuItem value="hello3">Hello</MenuItem>
<MenuItem value="hello4">Hello</MenuItem>
<MenuItem value="hello5">Hello</MenuItem>
<MenuItem value="hello6">Hello</MenuItem>
<MenuItem value="hello7">Hello</MenuItem>
<MenuItem value="hello8">Hello</MenuItem>
<MenuItem value="hello9">Hello</MenuItem>
<MenuItem value="hello10">Hello</MenuItem>
<MenuItem value="hello11">Hello</MenuItem>
<MenuItem value="hello12">Hello</MenuItem>
<MenuItem value="hello13">Hello</MenuItem>
<MenuItem value="hello14">Hello</MenuItem>
<MenuItem value="hello15">Hello</MenuItem>
<MenuItem value="hello16">Hello</MenuItem>
<MenuItem value="hello17">Hello</MenuItem>
<MenuItem value="hello18">Hello</MenuItem>
<MenuItem value="hello19">Hello</MenuItem>
<MenuItem value="hello20">Hello</MenuItem>
<MenuItem value="hello21">Hello</MenuItem>
</Select>
</FormControl>
</div>
</>
);
}
export default function App() {
return (
<div className="App" style={{ width: "400px" }}>
<div
id="my-dropdown"
className="dropdown"
style={{ marginLeft: "200px" }}
>
<a
id="my-btn"
href="#"
data-bs-toggle="dropdown"
data-bs-auto-close="outside"
aria-expanded="false"
>
Filter
</a>
<div
className="dropdown-menu dropdown-menu-sm-end"
aria-labelledby="my-btn"
>
<div>stuff to fill a bit</div>
<MySelect />
</div>
</div>
</div>
);
}
Link to try : https://codesandbox.io/s/lucid-babbage-5gfcqo?file=/src/App.js

How do I add a custom attribute to the generated React Menu item?

I'd to add a id or some attribute to the MenuItem, something like <MenuItem id="foo" onClick={handleClose}>Copy Ctrl+C</MenuItem> so that I can acess it from event.target in the context menu event handler in my TreeView.
Currently the code look like this:
export default function FileSystemNavigator() {
const [contextMenu, setContextMenu] = React.useState<{
mouseX: number;
mouseY: number;
} | null>(null);
const handleContextMenu = (event: React.MouseEvent) => {
// get the HTML element with the id, e.g., id="foo" in the attributes of event.target
event.preventDefault();
setContextMenu(
contextMenu === null
? {
mouseX: event.clientX + 2,
mouseY: event.clientY - 6
}
: // repeated contextmenu when it is already open closes it with Chrome 84 on Ubuntu
// Other native context menus might behave different.
// With this behavior we prevent contextmenu from the backdrop to re-locale existing context menus.
null
);
};
const handleClose = () => {
setContextMenu(null);
};
return (
<div onContextMenu={handleContextMenu} style={{ cursor: "context-menu" }}>
<TreeView
aria-label="file system navigator"
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
>
<TreeItem nodeId="1" label="Applications">
<TreeItem nodeId="2" label="Calendar" />
</TreeItem>
<TreeItem nodeId="5" label="Documents">
<TreeItem nodeId="10" label="OSS" />
<TreeItem nodeId="6" label="MUI">
<TreeItem nodeId="8" label="index.js" />
</TreeItem>
</TreeItem>
</TreeView>
<Menu
open={contextMenu !== null}
onClose={handleClose}
anchorReference="anchorPosition"
anchorPosition={
contextMenu !== null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
>
<MenuItem onClick={handleClose}>Copy Ctrl+C</MenuItem>
<MenuItem onClick={handleClose}>Delete</MenuItem>
<MenuItem onClick={handleClose}>Move</MenuItem>
<MenuItem onClick={handleClose}>Email</MenuItem>
</Menu>
</div>
);
}
Here's a live code example
There are a few options to achieve this:
by passing a different function to each of your MenuItem:
const handleCopy = () => {
// Do whatever you need
handleClose();
};
const handleDelete = () => {
// Do whatever you need
handleClose();
};
<MenuItem onClick={handleCopy}>Copy Ctrl+C</MenuItem>
<MenuItem onClick={handleDelete}>Delete</MenuItem>
you can use data- attributes:
const handleClose = (event) => {
const { action } = event.target.dataset
// Here can be a switch/case statement to iterate the action
setContextMenu(null);
};
<MenuItem data-action="copy" onClick={handleClose}>
Copy Ctrl+C
</MenuItem>
just use the inline function:
<MenuItem onClick={() => handleClose({action: 'copy'})}>Copy Ctrl+C</MenuItem>
One way you can have a span tag and pass an id to it inside your MenuItem like this :
<MenuItem onClick={handleClose}>
<span id="Copy Ctrl+C">Copy Ctrl+C </span>
</MenuItem>
<MenuItem onClick={handleClose}>
<span id="Delete">Delete </span>
</MenuItem>
<MenuItem onClick={handleClose}>
<span id="Move">Move </span>
</MenuItem>
<MenuItem onClick={handleClose}>
<span id="Email">Email </span>
</MenuItem>

Trigger modal from dropdown menu click event antd

So i have a <Modal/> which is controlled by a state Visible, i want to be able to change the state of this modal from a dropdown menu inside a table row from Antd using SetVisible but i cannot find a way to do it, any suggestions?
<Table // antd table
loading={Boolean(!data)}
rowKey={'id'} expandable={expandable}
pagination={{pageSize: 5}}
columns={columns}
dataSource={data}
/>
render: (text, record) => ( // fragment from column object, which contain in column that'll be rendered
<Space size="middle">
<Dropdown overlay={() => DropMenu()}> //Where the dropdown compoennt is being called
<a className="ant-dropdown-link" onClick={e => e.preventDefault()}>
Mais Opções <DownOutlined />
</a>
</Dropdown>
</Space>
const DropMenu = () => { //Dropdown Component
return (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/"> // Here I'll trigger the modal
trigger modal
</a>
</Menu.Item>
</Menu>
)
};
I actually realized that antd has a Modal.confirm() which is a function that renders a confirmation modal with an ok and cancel button which you handle your own way.
<Menu.Item danger>
<span onClick={() => confirm({id: record.id})}>Delete</span>
</Menu.Item>
function confirm({name, id}) {
Modal.confirm({
title: 'Confirm',
icon: <ExclamationCircleOutlined />,
content: 'Are you sure you want to delete this user??',
okText: 'Confirmar',
cancelText: 'Cancelar',
onOk: (close) => { ...handling deletion }
)}
}
you can also try
<Menu
items={[
{
key: 1,
label: <div onClick={() => edit()}>edit</div>,
},
]}
/>

Select Not Displaying Value (ReactJS/Material UI)

I am trying to create a multi-select form control, however, whenever I select something it does not get rendered. The function handleChange does get the event.target.value but it does not seem to add to the roleIds state. Furthermore, the console.log for the variable selected does not log anything to console.
Component Code:
const allRoleIds = [
"12345678",
"98765423",
"56465735683578",
];
const [roleIds, setRoleIds] = React.useState([]);
function handleChange(event) {
setRoleIds(event.target.value);
}
const [cassowaries, setCassowaries] = React.useState({
columns: [
{ title: "Cassowary Name", field: "name" },
{
title: "Cassowary Roles",
field: "roles",
render: (rowData) => {
return (
<li>
{rowData.roles.map((role) => (
<Chip label={role} className={classes.chip} />
))}
</li>
);
},
editComponent: (props) => (
<FormControl className={classes.formControl}>
<InputLabel>Roles</InputLabel>
<Select
multiple
value={roleIds}
onChange={handleChange}
input={<Input id="select-multiple-chip" />}
renderValue={(selected) => {
console.log(selected);
return (
<div className={classes.chips}>
{selected.map((value) => (
<Chip
key={value}
label={value}
className={classes.chip}
/>
))}
</div>
);
}}
// MenuProps={MenuProps}
>
{allRoleIds.map((id) => (
<MenuItem key={id} value={id}>
{id}
</MenuItem>
))}
</Select>
</FormControl>
),
},
{ title: "Penguin", field: "penguin" },
],
data: [{ name: "Mehmet", roles: roleIds, penguin: true }],
});
You are doing concat in your handleChange. Material ui already gives you the array of selected values to you. Sp fix your handleChange everything should be fine.
Like this
function handleChange(event) {
setRoleIds(event.target.value);
}
Working demo is here
EDIT:
Based on additional req (see comments):
If a custom multi select component needs to be used inside materail table editcomponent, then the state of select should not be managed outside but state & onChagne needs to managed inside editComponent using the prop it provides.
See working demo here
Code snippet - material ui table
...
columns: [
{ title: "Tag Name", field: "name" },
{
title: "Tag Roles",
field: "roles",
render: rowData => {
return (
<li>
{rowData.roles.map(role => (
<Chip label={role} className={classes.chip} />
))}
</li>
);
},
editComponent: props => {
console.log("props", props);
return (
<FormControl className={classes.formControl}>
<InputLabel>Roles</InputLabel>
<Select
multiple
value={props.value}
onChange={e => props.onChange(e.target.value)}
input={<Input id="select-multiple-chip" />}
renderValue={renderChip}
>
{allRoleIds.map(id => (
<MenuItem key={id} value={id}>
{id}
</MenuItem>
))}
</Select>
</FormControl>
);
}
},
{ title: "Penguin", field: "penguin" }
],
data: [{ name: "Mehmet", roles: [], penguin: true }]
...

Using Grid with SortableJS

I am using SortableJS for React and I am trying to implement horizontal Grid, so that the final result would look like this:
I managed to do that, however the sort function does not work at all. I think it has something to do with the tag attribute. When I try tag={Grid} I get following error:
Sortable: el must be an HTMLElement, not [object Object]
Any ideas how can I implement Grid to the tag attribute? Here is my code:
<Grid container>
<Sortable options={{ fallbackOnBody: true, group: "items", handle: reorderHandle }} tag={Grid} onChange={handleChange}>
{items.map((item, index) =>
<Paper key={index} data-id={index} className={classes.item} elevation={0}>
<ButtonHelper {...getHandleClass(editable)} key={index} icon={
<Badge badgeContent={index + 1} color="default">
{editable && <ReorderIcon />}
</Badge>}
/>
<Grid item xs={4} key={index}>
<div className={classes.gridItem}>
<GalleryInput className={classes.image} style={{ width: '300px' }} label="image" source={`${source}[${index}].image`} />
<br></br>
<TextInput label="desc" source={`${source}[${index}].desc`} />
{editable && <ButtonHelper icon={<RemoveIcon />} onClick={handleRemove(index)} className={classes.left} />}
</div>
</Grid>
</Paper>
)}
</Sortable>
</Grid>
Thank you for any help.
Since you are using a custom component, the tag prop accepts it as a forwarRef component only.
So you need to update in that way.
Sample Snippet
// This is just like a normal component, but now has a ref.
const CustomComponent = forwardRef<HTMLDivElement, any>((props, ref) => {
return <div ref={ref}>{props.children}</div>;
});
export const BasicFunction: FC = props => {
const [state, setState] = useState([
{ id: 1, name: "shrek" },
{ id: 2, name: "fiona" }
]);
return (
<ReactSortable tag={CustomComponent} list={state} setList={setState}>
{state.map(item => (
<div key={item.id}>{item.name}</div>
))}
</ReactSortable>
);
};

Resources