Handling icon onClick events with Material-UI and ReactJS - reactjs

I'm trying to get the "name" of the icon button when clicked
I've read about using material UI's but I keep getting "undefined" from the handler
import React from 'react'
import {IconButton} from '#material-ui/core'
import InfoIcon from '#material-ui/icons/InfoOutlined'
const App = () => {
const handleIconClicks = (e) => {
console.log(e.target.name)
}
return (
<div>
<IconButton name="details" onClick={(e) => handleIconClicks(e)}>
<InfoIcon />
</IconButton>
</div>
)
}
export default App
handleIconClicks() should return the name of the event.target, instead I get undefined

event.target may refer to the icon in case of <IconButton />.
You should be able to safely use event.currentTarget to get the button (and event.currentTarget.name to get its name).

You could not get the name using the event of the IconButton because its event is null.
So you could it using this.
const handleIconClicks = name => () => {
console.log(name);
}
... ... ...
<IconButton name="details" onClick={handleIconClicks('detail')}>
<InfoIcon />
</IconButton>
... ... ...

Related

how to call child component callback event from parent component in react

Hi I am a beginner in React, I am using Fluent UI in my project .
I am planning to use Panel control from Fluent UI and make that as common component so that I can reuse it.I use bellow code
import * as React from 'react';
import { DefaultButton } from '#fluentui/react/lib/Button';
import { Panel } from '#fluentui/react/lib/Panel';
import { useBoolean } from '#fluentui/react-hooks';
export const PanelBasicExample: React.FunctionComponent = () => {
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
return (
<div>
<Panel
headerText="Sample panel"
isOpen={isOpen}
onDismiss={dismissPanel}
// You MUST provide this prop! Otherwise screen readers will just say "button" with no label.
closeButtonAriaLabel="Close"
>
<p>Content goes here.</p>
</Panel>
</div>
);
};
https://developer.microsoft.com/en-us/fluentui#/controls/web/panel#best-practices
I remove <DefaultButton text="Open panel" onClick={openPanel} /> from the example .
So my question is how can I open or close this panel from any other component ?
I would use React useState hook for this.
Make a state in the component that you want render the Panel like
const [openPanel, setOpenPanel] = useState({
isOpen: false,
headerText: ''
})
Lets say for example you will open it from button
<Button onClick={() => setOpenPanel({
isOpen: true,
headerText: 'Panel-1'
})
}> Open me ! </Button>
Then pass the state as props to the Panel component
<PanelBasicExample openPanel={openPanel} setOpenPanel={setOpenPanel} />
in PanelBasicExample component you can extract the props and use it.
export const PanelBasicExample(props) => {
const {openPanel, setOpenPanel} = props
const handleClose = () => {setOpenPanel({isOpen: false})}
return (
<div>
<Panel
headerText={openPanel.headerText}
isOpen={openPanel.isOpen}
onDismiss={() => handleClose}
// You MUST provide this prop! Otherwise screen readers will just say "button" with no label.
closeButtonAriaLabel="Close"
>
<p>Content goes here.</p>
</Panel>
</div>
);
}

React.js: How to close headlessui Disclosure modal from code?

I encountered an issue trying to close the headlessui Disclosure modal inside the panel.
My goal is to have a button inside the panel which can close the modal.
The way I tried to solve this problem is doing it manually using useRef, but it works partially.
After opening the panel for the first time, you can close the modal but if you try to open it again, it doesn't work. Can't figure out how to solve this issue.
Any help will be appreciated.
Here is the codesandbox link
And here is the code
import { Disclosure } from "#headlessui/react";
import React, { useState, useRef } from "react";
import CloseIcon from "#material-ui/icons/Close";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
const App = () => {
const [isClosed, setIsClosed] = useState(false);
const modalRef = useRef(null);
const hideModalHandler = (e) => {
e.preventDefault();
modalRef.current?.click();
setIsClosed(!isClosed);
};
return (
<Disclosure>
{({ open }) => (
<div ref={modalRef}>
<Disclosure.Button>
<span>modal</span>
<ExpandMoreIcon />
</Disclosure.Button>
{!isClosed && (
<Disclosure.Panel>
<CloseIcon onClick={hideModalHandler} />
<div>name</div>
</Disclosure.Panel>
)}
</div>
)}
</Disclosure>
);
};
export default App;
I haven't used headlessui Disclosure but I see that the function hideModalHandler isn't actually hiding but toggling. Did you mean setIsClosed(true) instead of setIsClosed(!isClosed)?
Also, after a quick look at the documentation, have you tried using the close from the headlessui Disclosure? You don't need useRef
Use the state, and wrap the disclosure button into a DIV
with onClick and some ID string to identify what disclosure must be open. Something like this (works for multiple disclosures):
const [keyOfOpenDisclosure, setKeyOfOpenDisclosure] = useState('')
const toggleDisclosure = (key: string) => {
setKeyOfOpenDisclosure((prev) => (prev !== key ? key : ''))
}
...
<Disclosure>
<div onClick={() => toggleDisclosure(someId)}>
<Disclosure.Button>
Text of disclosure button
</Disclosure.Button>
</div>
<Transition
show={someId === keyOfOpenDisclosure}
...

Changing button label on click with grommet?

Does anyone have any insight into changing button label on click when using grommet UI and styled components? Is it easier to just use a custom button rather than grommets?
Here are a few examples on how to make the Button label change onClick action:
import React, { useState } from "react";
import { render } from "react-dom";
import { grommet, Box, Button, Grommet, Text } from "grommet";
const App = () => {
const [label, setLabel] = useState(1);
const [name, setName] = useState("shimi");
const flipName = name => {
return name === "shimi" ? setName("shai") : setName("shimi");
};
return (
<Grommet theme={grommet}>
<Box pad="small" gap="small" width="small">
// label is a number that is being increased on every click event
<Button
label={label}
onClick={() => {
setLabel(label + 1);
}}
/>
// label string is controlled with external logic outside of the button.
<Button
label={<Text weight="400">{name}</Text>}
onClick={() => {
flipName(name);
}}
/>
</Box>
</Grommet>
);
};
render(<App />, document.getElementById("root"));
In addition to the examples above, in Grommet, you don't have to use the label prop and you can leverage the Button children to control the way your Button display.

Material UI ClickAwayListener close when clicking itself

I have the below display sidebar Switch that shows up within a Popper. So, Ideally, if you click elsewhere (outside of the Popper element), Popper should disappear. If you click inside Popper element, it should not go anywhere. When I click on the Switch or Display Sidebar text, that Popper goes away. I wrapped the Popper with <div> it didn't help either.
Popper https://material-ui.com/api/popper/
Switch https://material-ui.com/api/switch/
ClickAwayListener https://material-ui.com/utils/click-away-listener/
Below is the Popper Code
<ClickAwayListener onClickAway={this.handleClickAway}>
<div>
<Popper className={classes.popper} id={id} open={open} placement="bottom-end" anchorEl={anchorEl} transition>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Paper className={classes.SliderBox}>
<Switch
checked={this.state.checkedB}
onChange={this.handleChange('checkedB')}
value="checkedB"
color="primary"
onClick={handleDrawer}
className={classNames(classes.menuButton, sidebar && classes.hide)}
/>
Display Sidebar
</Paper>
</Fade>
)}
</Popper>
</div>
</ClickAwayListener>
I have the sample here (though I couldn't get it work I don't know why it gives error on clickaway)
https://codesandbox.io/s/8pkm3x1902
By default, Popper leverages a portal (https://github.com/mui-org/material-ui/blob/v4.11.0/packages/material-ui/src/Popper/Popper.js#L202) which renders its content in a separate part of the DOM (as a direct child of the <body> element) from where the Popper element is. This means that you have the ClickAwayListener around an empty <div>, so a click anywhere (including within the Popper content) will be considered to be outside of that empty <div>.
Moving the ClickAwayListener directly around the Fade (rather than around the Popper) ensures that it is surrounding the actual content rendered by the Popper.
Here's a working example based on your sandbox:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Popper from "#material-ui/core/Popper";
import Fade from "#material-ui/core/Fade";
import Paper from "#material-ui/core/Paper";
import Switch from "#material-ui/core/Switch";
import Avatar from "#material-ui/core/Avatar";
import ClickAwayListener from "#material-ui/core/ClickAwayListener";
class App extends Component {
state = {
anchorEl: null,
open: false,
checkedB: true
};
handleClick = (event) => {
const { currentTarget } = event;
this.setState((state) => ({
anchorEl: currentTarget,
open: !state.open
}));
};
handleChange = (name) => (event) => {
this.setState({
[name]: event.target.checked
});
};
handleClickAway = () => {
this.setState({
open: false
});
};
render() {
const { anchorEl, open } = this.state;
const { handleDrawer } = this.props;
const id = open ? "simple-popper" : null;
return (
<div className="App">
asdsadsa
<Avatar
alt="Test"
src="https://www.nretnil.com/avatar/LawrenceEzekielAmos.png"
style={{ margin: "0 10px" }}
onClick={this.handleClick}
/>
<div>
<Popper
id={id}
open={open}
placement="bottom-end"
anchorEl={anchorEl}
transition
>
{({ TransitionProps }) => (
<ClickAwayListener onClickAway={this.handleClickAway}>
<Fade {...TransitionProps} timeout={350}>
<Paper //className={classes.SliderBox}
>
<Switch
checked={this.state.checkedB}
onChange={this.handleChange("checkedB")}
value="checkedB"
color="primary"
onClick={handleDrawer}
/>
Display Sidebar
</Paper>
</Fade>
</ClickAwayListener>
)}
</Popper>
</div>
</div>
);
}
}
export default App;
ReactDOM.render(<App />, document.getElementById("root"));
In v4 the ClickAwayListener supports recognizing that elements are within its React element tree even when they are rendered within a portal (the original question was for v3), but it is still more reliable to put the ClickAwayListener inside the Popper rather than outside to avoid the ClickAwayListener firing on the click to open the Popper which then makes it look like the Popper isn't working since it immediately closes (example: https://codesandbox.io/s/popper-clickawaylistener-forked-x4j0l?file=/src/index.js).
For me, just making the popupOpen=false not solved the problem. So, I have to set the anchorElement to null as well, like so
const handleClickAway= ()=>{
popupOpen=false; // Here you can also use state hook, but in my case, I'm using simpe boolean variable.
setAnchorEl(null);
}

Material-ui dialog is not closing when overlay is clicked

I'm using material-ui's dialog. When the overlay is clicked, handleClose is not called. When the "Esc" button is pressed, it is called.
I've injected tap events. What else is wrong?
import React, { Component, PropTypes } from 'react';
import Dialog from 'material-ui/Dialog';
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
// Tap event required
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin();
class MyComponent extends Component {
handleClose(){
console.log('I will be closed');
}
render() {
return (
<div>
<h1>Modal test</h1>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<Dialog
title="Test Dialog"
modal={false}
open={(this.props.active)}
onRequestClose={this.handleClose}
autoScrollBodyContent={true}>
Hello world, I'm a dialogue.
</Dialog>
</MuiThemeProvider>
</div>
);
}
}
you must define onClose prop for dialog. as you can see:
const [opendialog , setOpendialog] = React.useState(false);
const handleOpen = () => {
setOpendialog(true);
};
const handleClose = () => {
setOpendialog(false);
}
return (
<>
<Button onClick={handleOpen}>open dialog!</Button>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>
<Typography>
are you sure?
</Typography>
</DialogTitle>
<Button onClick={handleClose}>No</Button>
</Dialog>
</>
);
You have to define a function for closing the dialog, and send it with prop onClose to Dialog component.
One thing you have to keep in mind, is that, props disableBackdropClick should not be passed to Dialog component.
The sample code is as follow
<Dialog
open = { props.open }
// disableBackdropClick
onClose = {(event, reason) => {
if (props.onClose) {
props.onClose(event, reason);
}
}}
>
{ ...props.children }
</Dialog>
According to specs you should not have to do this, but you can use the following property to force close :
<Dialog onBackdropClick={YOUR_CLOSE_FUNCTION_HERE} />
I just tried it out and it works fine. Is there any other code relevant to this? Maybe re-install material ui and try again.

Resources