I am trying to add a FormControl, a Select component and a MenuItem to the action prop of the CardHeader IconButton.
Currently the code looks like this:
Rendering:
<CardHeader
action={
<IconButton
onClick={this.renderFilterRequest()}
>
<Edit />
</IconButton>
}
/>
onClick method:
renderFilterRequest() {
const { selection } = this.state;
return (
<div>
<FormControl>
<Select
value={selection}
onChange={this.handleFilterChange}
>
<MenuItem value='1'>January</MenuItem>
<MenuItem value='2'>February</MenuItem>
</Select>
</FormControl>
</div>
);
}
The error I get is onClick listener to be a function, instead got a value of object type. What is the right way to render the dropdown menu on CardHeader action click?
You are returning some div from from this.renderFilterRequest, And you are also calling the function, so the value of onClick becomes the div. But they were meant to be functions, right?
So it should have been just: onClick={this.renderFilterRequest}.
This function also returned a div but there is no way to attach it to rendering logic in render.
You need to put the MenuItems in your render method and show/hide them depending on the state.
Your onClick listener should be a function that changes the state so that the MenuItems become visible.
Here is a simple demo how this should be done:
Related
I have the following code to use React To print, a library to print a component:
<ReactToPrint
trigger={() =>
<SqIconButton action={(e)=>e} color={"success"}>
<AiIcons.AiFillPrinter style={{color: 'black',fontSize:'1rem'}}/>
</SqIconButton>
}
content={() => componentRef.current}
/>
And my custom Button SqIconButton reads as follows:
export default function SqIconButton({children,color="primary",fontColor="#000000",action,tip="",disableElevation=false,sxstyle={},classes}){
return(
<Tooltip title={tip}>
<Button
color={color}
variant="contained"
onClick={action}
disableRipple={disableElevation}
className={`TBButton ${classes}`}
sx={sxstyle}
>
{children}
</Button>
</Tooltip>
)
}
In the examples of React To Print code for its trigger nothing is referencing "onclick" property on buttons, examples mostly look like this: <button>print this</button> and if I use this element it actually works. But my custom button does not work as is, so I think I have to pass the onclick event over my action custom property, so I'm trying with action={(e)=>e}, but the button does nothing when I press it.
Documentation says about trigger:
A function that returns a React Component or Element. Note: under the hood, we inject a custom onClick prop into the returned Component/Element. As such, do not provide an onClick prop to the root node returned by trigger, as it will be overwritten
So I don't know if onclick will be overwritten how could I pass this event to a child button component.
I'm trying to hide element when button is clicked or when checkbox is checked. I tried to Google the solution but can't find anything. Can anyone help with this?
I want to hide that first MenuItem which has Input inside of it and it needs to be hidden when Button is clicked which is inside of second MenuItem or Checkbox is checked which is inside of third MenuItem. Also it needs to return back to normal when clicking Button again, or when checkbox is not checked.
<MenuItem>
<Input />
<MenuItem/>
<MenuItem>
<Button onClick={handleClick}>
Hide onClick!
</Button
</MenuItem
<MenuItem>
<Checkbox />
</MenuItem>
I assume I need to do something like this. Inside handleClick I need to set input MenuItems display to none.
const [hide, setHide] = useState(false);
const handleClick = () => {
}
So basically I think I need to change style of element onClick or checked. I don't have much to show because I don't have any clue how to do that.
change this
<MenuItem>
<Input />
<MenuItem/>
to this
{ hide && (<MenuItem>
<Input />
<MenuItem/>) }
and the function must be like this
const handleClick = () => {
setHide(prev => !prev)
}
use the conditional rendering for this.
{hide && (
<MenuItem>
<Input />
</MenuItem>
)}
now, whenever the hide state is true, <Input/> menuItem will be rendered and for false, it'll be hidden.
To mutate the hide state from both CheckBox and Button, create a handler that will set the state.
const toggleHide = () => {
setHide((oldState) => !oldState);
};
Now call this toggleHide function on the click of the Button and onChange of the Checkbox.
Note:- for better result onClick listeners can be added to the MenuItem instead of the Button and CheckBox.
I've been trying to understand and write code on the Box component in material-UI. (https://material-ui.com/components/box/#box)
I've been trying to override a Button component the two ways it describes in the documentation, but I have no idea how. When I run the code segment using both methods, the button appears but no color change. Then when I try to add an extra Button underneath the clone element code segment I get an error saying 'Cannot read property 'className' of undefined'.
<Box color="primary" clone>
<Button>Click</Button>
<Button>Click</Button>
</Box>
When I add a Button component underneath in the second render props way, the first button just disappears from the DOM completely.
<Box color="secondary">
{props => <Button {...props} > Click </Button>}
<Button color="secondary">Click</Button>
</Box>
Would appreciate an explanation of how overriding underlying DOM elements work.
There are a few issues with the code you've shown in your question.
primary and secondary are not valid colors within the palette. They are valid options for the color prop of Button, but here you are trying to reference colors within the theme's palette object. For this purpose, you need primary.main and secondary.main (which is what Button uses when you specify <Button color="primary">).
Box only supports a single child when using the clone property and it only supports a single child when using the render props approach. In both of your examples you have two children.
Here is the Material-UI source code that deals with the clone option:
if (clone) {
return React.cloneElement(children, {
className: clsx(children.props.className, className),
...spread,
});
}
This is creating a new child element that combines the className generated by Box with any existing class name on the child. It gets at this existing class name via children.props.className, but when there are multiple children then children will be an array of elements and will not have a props property so you get the error:
Cannot read property 'className' of undefined
Here is the Material-UI source code that deals with the render props approach:
if (typeof children === 'function') {
return children({ className, ...spread });
}
When you have more than one child, then typeof children === 'function' will not be true and it won't use the render props approach. In this case, both children just get normal react rendering and trying to render a function doesn't render anything.
Below is a working example that fixes all of these problems by using a single Button child in the clone case and a single function child in the render props case (a function that then renders two Button elements).
import React from "react";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
export default function App() {
return (
<>
<Box color="primary.main" clone>
<Button>Click</Button>
</Box>
<Box color="secondary.main">
{props => (
<>
<Button {...props}> Click </Button>
<Button color="secondary">Click</Button>
</>
)}
</Box>
</>
);
}
I want to add accessibility features to the Material UI Button.
I expect to use this custom button as follows:
import Button from '#material-ui/core/Button';
function AccessibleButton(props) {
const { accessKey, ariaLabel, isDisabled, label, onClick, tabIndex, variant, size} = props;
return (
<Button
accesskey={accessKey}
aria-label={ariaLabel}
disabled={isDisabled}
className={componentCls}
onClick={onClick}
tabindex={tabIndex}
variant={variant}
size={size}
>
{label}
</Button>
);
};
Aria labels are available for inputs, but don't seem to be for buttons. How do I pass the additional props (accessKey, ariaLabel) into the Material UI Button. How do I do this?
This should work since most of our components forward their excess props. On the corresponding api pages (here https://material-ui.com/api/button/) you will find a table with the apparent props. Below that is a note that tells you what happens with excess props.
It's a bit iffy to navigate (we're working on it) but in the end you'll see that excess props are forwarded to the native element. So <Button aria-label="ariaLabel" /> will render a <button aria-label="ariaLabel" />.
Your code should work.
I have created sandbox where you can inspect the button and see the button will have aria-label and accesskey attribute.
<Button
aria-label="This is aria label"
accessKey="Key"
variant="contained"
color="primary"
>
I'm a button
</Button>
Try inspecting button in below sandbox.
I am using the Material UI library, and cannot figure out if there is a way to have 2 different click events happening on a List Item.
This is basically what I have:
<ListItem
leftAvatar={
<div onClick={() =>
insideAvatarFunction()}
>
{<Avatar />}
</div>
}
primaryText={primaryText}
onTouchTap={() => everywhereClickFunction}
/>);
The onTouchTap triggers, and ignores the onClick inside of the leftAvatar. If the outer onTouchTap is not there, then the onClick triggers fine.
Is there a way to have the onTouchTap to trigger when anywhere BESIDES the avatar is clicked?
Thanks!
Event.stopPropagation()
Prevents further propagation of the current event in the capturing and bubbling phases.
import React from 'react';
import {List, ListItem} from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
export default class App extends React.Component {
insideAvatarFunction(e) {
e.stopPropagation();
console.log('Fired insideAvatarFunction()!');
}
everywhereClickFunction(e) {
console.log('Fired everywhereClickFunction()!');
}
render () {
return (
<List>
<ListItem primaryText="Inbox"
leftAvatar={
<div onClick={this.insideAvatarFunction}>
<Avatar />
</div>
}
onTouchTap={this.everywhereClickFunction}
onClick={this.everywhereClickFunction}
/>
</List>
);
}
}
So when clicked on the Avatar's node, the e.stopPropagation() line in the insideAvatarFunction should prevent the onClick/onTouchTap to further propagate to the parent's node which is the ListItem component.
Here is an interesting read about Javascript Events Order and event bubbling.
In case anyone else comes across this question, the comment by #zv.diego solved the issue.
I put stopPropagation() inside the function of the div's onClick, and switched the parent to onClick instead of onTouchTap and it works!
You can use Avtar wrapped with IconButton.
import { IconButton, Avatar } from '#mui/material'
<IconButton
onClick={() => alert('Hiii')}>
<Avatar
src={userProfilePic(post.user)}
sx={{ bgcolor: red[500] }}
aria-label="recipe">
post.user.firstName[0] + post.user.lastName[0]}
</Avatar>
</IconButton>