Pass icon button as props using react - reactjs

I have a common grid component where I define the structure of the grid and also structure of a button bar that goes on top of the grid.
Common-grid.js
<Box height='100%' width='100%' position='absolute'>
<div className="common-grid">
<div className="button-bar">
</div>
<div className="ag-grid">
</div>
</div>
</Box>
I pass data to the grid from my other component based to fill in the grid.
MyComponent.js
{gridData.length > 0 && <Grid tableData={gridData} columnData={activeListColumnDef} {...props}/>}
Along with the data, I would also like to pass icon buttons that I would like to see in button bar.
<IconButton
icon={<AddIcon />}
onClick={onClickOpenActiveListEditor}
/>
<IconButton
icon={<EditIcon />}
/>
I do not want to define icon buttons in the common component but pass it as props. Is it possible to pass such html elements along with its event listeners as props? Please help!

Sure, it's called a render prop. Just directly pass the node like this:
// in the parent component
<Grid
tableData={gridData}
columnData={activeListColumnDef}
icon={<AddIcon onClick={onClickOpenActiveListEditor} />}
{...props}
/>
// in the Grid component
function Grid({tableData, columnData, icon}){
return (
<>
// grid stuff
{icon && icon}
</>
)
}
If you need typescript support, the node would typed as such:
interface GridProps{
// stuff
icon?: React.ReactNode;
}

You could do something like:
const renderIcon = (onClick) => {
return <Icon onClick={onClick} />
}
...
<IconButton renderIcon={renderIcon} />
Then, inside IconButton:
{renderIcon()}

Related

Cannot display switch in tabs in material-ui

I need my tabs to have a tab consisting of a switch but when I implemented it as it was said, I am not able to see it there.
Maybe it's hidden underneath but I tried changing its z-index.
Pass your Switch component as the label prop.
...
<Tab
component="span"
label={
<Switch
checked={isSwitchOn}
onChange={(e) => setSwitch(!isSwitchOn)}
name="toggleType"
/>
}
/>
Tab component does not display children props inside the MuiTab-wrapper element.
Maybe you can use icon props.
<Tab
component="span"
icon={
<Switch
checked={isSwitchOn}
onChange={(e) => setSwitch(!isSwitchOn)}
name="toggleType"
/>
}
/>

How to reset useState everytime I change my page?

I'm working on a Hamburger Navbar
here is my page look like:
the issues is whenever I clicked to another route but the navbar still appear on right side. I want to make it whenever I go to another route it will disappear.
Here is my code:
const App = (props) => {
const [ menuOpen, setMenuOpen ] = useState(false);
<HamburgerNav>
<HamburgerNavContainer>
<LogoLinkStyled to="/">
<LogoNav src="https://thebeuter.com/wp-content/uploads/2020/04/logo-black.png" />
</LogoLinkStyled>
<HamburgerUtilities>
<HamburgerUlityItem>
<Icon className="fal fa-search fa-rotate-90" onClick={openModalHandler} />
</HamburgerUlityItem>
<HamburgerUlityItem>
<Link to="/cart" style={{ color: 'black', textDecoration: 'none' }}>
<Icon className="fal fa-shopping-bag" />
<CartNumb>({props.basketProps.basketNumbers})</CartNumb>
</Link>
</HamburgerUlityItem>
<HamburgerUlityItem>
<HamburgerLine onClick={() => setMenuOpen(!menuOpen)} />
</HamburgerUlityItem>
</HamburgerUtilities>
</HamburgerNavContainer>
</HamburgerNav>
How can I fix this problem? Really appreciate it.!!!
Github project: https://github.com/nathannewyen/the-beuter
update Router code:
Here is my Router for all routes in navbar
<Router>
<ContactForm path="/contact" />
<SizeChart path="/size-chart" />
<ShippingAndReturn path="/shipping-return" />
<PrivacyAndPolicy path="/privacy-policy" />
<AboutUs path="/about-us" />
<ShopAllProducts path="/" />
<NewArrival path="/shop/new-arrival" />
<Tops path="/product-category/top" />
<Bottoms path="/product-category/bottom" />
<Product path="/product/:title_url" />
<SearchInfo path="/search/:title" searchTerm={searchTerm} title="Profile" />
<Cart path="/cart" />
<Checkout path="/checkout" />
</Router>
You need to add onClick to the "route" component that is not closing it and do something like this:
onClick={() => { setMenuOpen(prevState => {return !prevState}) }}
If for example you want it to close when you click on "T-Shirts", then "T-Shirts" must also have that onClick.
If you already have onClicks on these components with another function, you can just add multiple functions inside the anonymous function like this:
onClick={() => {
YourOtherFunction();
setMenuOpen(prevState => {return !prevState});
}
If your components are not in the App.js you need to somehow pass the onClick down too them.
Since its a state you wont be able to pass down the setMenuOpen itself, you need a wrapper function. So first create the wrapper:
const setMenuOpenWrapper = () => {
setMenuOpen(prevState => return { !prevState });
}
Then pass it down to the childrens like:
and then inside your ContactForm on the link to the contact form add the onClick:
...onClick={() => { closeMenuFunction(); }}
Ok I just checked your code, you need to pass down the function to the Sidenav component.
So in your App.js first create the wrapper function as I explained above
After that again in App.js on line 316 where you have <SideNav menuOpen={menuOpen} /> change it to <SideNav menuOpen={menuOpen} closeMenuFunction={setMenuOpenWrapper}/>
Then in your Sidenav.jsx on all of your menu items add an onclick:
onClick={props.closeMenuFunction}
And then it should work.

How to use <br/> in string to put string in new line?

I was creating React project and here is the component that I want to ensure that is put inside a string.
<InfoIcon
tooltip={`${hints.todoHint} <br/> ${hints.inProgressHint} <br/> ${hints.doneHint}`}
/>
but it is not working since br/ is literally rendered like br/
One option is to convert tooltip in to three props and add <br /> in the InfoIcon component. For example InfoIcon component can be
const InfoIcon = ({ todoHint, inProgressHint, doneHint }) => (
<div>
{todoHint}
<br />
{inProgressHint}
<br />
{doneHint}
</div>
);
// Using Info Icon
<InfoIcon todoHint={todoHint} inProgressHint={inProgressHint} doneHint={doneHint} />
Other option is to send tooltip as follows
const tooltip = (
<div>
{hints.todoHint}
<br />
{hints.inProgressHint}
<br />
{hints.doneHint}
</div>
)
<InfoIcon tooltip={tooltip} />
Well, material-ui tooltip allows you to use ANY kind of HTML content. => refer to official document customized tooltiops
This means you can use normal <div> and <Typography />, and any other styled elements to handle multi-line content.
The only thing you need to do is pass the content to props title => refer to document of tooltip api
import {
Tooltip,
Typography
} from '#material-ui/core';
<Tooltip
title={ // customized content here via props `title`
<>
<div>Seperate line</div>
<Typography>Seperate line</Typography>
</>
}
>
<IconButton aria-label="delete">
<InfoIcon /> // You can use your original icon here
</IconButton>
</Tooltip>
Watch it online: https://stackblitz.com/run
You can find related question here: How to make line break for ToolTip titles in Material-UI

material-ui v4.0.1 warning "Expected an element that can hold a ref"

I upgraded to material-ui v4.0.1 and I see that Modals now require a forwarded ref. I'm having some trouble implementing a fix for this using class components and Dialogs.
I tried using React.createRef() as well as React.forwardRef((props, ref) => (...) in a few places but I can't figure out how to resolve this warning.
In my parent component I render a custom component
<ApolloFormDialog />
In ApolloFormDialog I render essentially:
<Dialog ...>
{title}
{subtitle}
{form}
</Dialog>
The full warning is Warning: Failed prop type: Invalid prop 'children' supplied to 'Modal'. Expected an element that can hold a ref. Did you accidentally use a plain function component for an element instead?
However I am using class components currently. Migrating to use function components is not an option right now as my app is rather large.
I have tried adding a ref to ApolloFormDialog as
<ApolloFormDialog ref={React.createRef()} />
as well as wrapping ApolloFormDialog's class with:
export default React.forwardRef((props, ref) => <ApolloFormDialog ref={ref} {...props}/>)
and then adding that ref to the dialog as
<Dialog ref={this.props.ref} />
but the warning still persists, and I'm not sure where to go from here.
My issue didn't actually have to do with Dialog, but with the prop TransitionComponent on Dialog.
I switch between two types of transitions in my ApolloFormDialog depending on if the screen is below a certain breakpoint, which was being called as function components:
<Dialog
open={open}
onClose={onRequestClose}
classes={{
paper: classnames(classes.dialogWidth, classes.overflowVisible),
}}
fullScreen={fullScreen}
TransitionComponent={
fullScreen ? FullscreenTransition : DefaultTransition
}
>
{content}
</Dialog>
FullscreenTransition and DefaultTransition come from a file and are defined as follows:
import React from 'react'
import Fade from '#material-ui/core/Fade'
import Slide from '#material-ui/core/Slide'
export function DefaultTransition(props) {
return <Fade {...props} />
}
export function FullscreenTransition(props) {
return <Slide direction='left' {...props} />
}
export function FullscreenExpansion(props) {
return <Slide direction='right' {...props} />
}
Changing these functions to the following fixed my issue:
import React from 'react'
import Fade from '#material-ui/core/Fade'
import Slide from '#material-ui/core/Slide'
export const DefaultTransition = React.forwardRef((props, ref) => (
<Fade {...props} ref={ref} />
))
export const FullscreenTransition = React.forwardRef((props, ref) => (
<Slide direction='left' {...props} ref={ref} />
))
export const FullscreenExpansion = React.forwardRef((props, ref) => (
<Slide direction='right' {...props} ref={ref} />
))
This was a relatively hard issue to solve on my end, so I'm going to leave this question up just in case someone else runs into a similar issue somewhere down the road.
I have had the same problem with "#material-ui/core/Tooltip" wrapping a new functional component. Even if the component was wrapped in a div inside its own code.
<!-- "Did you accidentally use a plain function component for an element instead?" -->
<Tooltip>
<NewFunctionalComponent />
</Tooltip>
<!-- Wrapped in a new div, devtools won't complain anymore -->
<Tooltip>
<div>
<NewFunctionalComponent />
</div>
</Tooltip>
<!-- No more warnings! -->
Wrapping my component to another div inside material-ui transition componet (like Slide, Fade etc) solves my issue.
Example code:
From
<Slide direction='Right' in = {isSideNavOpen} mountOnEnter unmountOnExit>
<MyComponent />
</Slide>
To
<Slide direction='Right' in = {isSideNavOpen} mountOnEnter unmountOnExit>
<div><MyComponent /></div>
</Slide>`
React.forwardRef fixes the issue for me, too.
To ensure that the React.forwardRef solution works with Typescript, you must implement the following as per the documentation in Material-UI:
const Transition = React.forwardRef<unknown, TransitionProps>(function Transition(props, ref) {
return <Slide ref={ref} {...props} />;
});
See the source code in their demo here.
I was getting the same error
expected an element that cand hold a ref
I solved by adding a <Box></Box> surrounding my children component.
From
<Modal open={open} onClose={handleClose}>
<DetailsCard />
</Modal>
To
<Modal open={open} onClose={handleClose}>
<Box>
<DetailsCard />
</Box>
</Modal>
I had a similar problem and I solved it by wrapping my functional component in a div.
In my case the problem ocurred with a Tooltip component wrapping a custom functional component.
<Tooltip tittle={...}>
<CustomFC />
</Tooltip>
And the solution was
<Tooltip tittle={...}>
<div>
<CustomFC />
</div>
</Tooltip>

Using the Controller components, what is needed to show the Label in the TextFields?

Using React-admin, I am using the ShowController component, which gives me more freedom to customize the ShowView. However, I would like to keep seeing the labels in the TextFields, but, they are gone.
This piece of code shows how I am using the ShowController and, it works partially: only show the record values, not the labels (I also tried without the prop "label", it doesn't work neither).
const OrderShow = props => {
return (<ShowController {...props}>
{controllerProps => {
return (
<Grid container spacing={8}>
<Grid item xs={3}>
<TextField label="ID" source="id" {...controllerProps} />
...
What is missing to show the labels as in the ShowView standard component?
TextFields' labels are usually filled by their source property.
If you want to use <ShowController> component with custom layout, I suggest you to create another custom component and use it inside of <Show>, <ShowView> or <SimpleShowLayout>.
I wrapped the fields with SimpleForm component to be able to show their labels and hide the Toolbar with custom CardActions.
Example:
const FormToolbar = () => (
<CardActions style={{display: 'none'}}>
</CardActions>
);
const FormDiv = ({controllerProps, ...props}) => (
<Grid container spacing={24}>
<Grid item xs={12}>
<SimpleForm toolbar={<FormToolbar/>}>
<TextField {...props} record={controllerProps.record} source="name"/>
</SimpleForm>
</Grid>
</Grid>
);
const OrderShow = props => (
<ShowController {...props} title="Order">
{controllerProps =>
<Show actions={<ShowActions pageType="show" />} {...props} {...controllerProps}>
<SimpleShowLayout>
<FormDiv controllerProps={controllerProps} />
</SimpleShowLayout>
</Show>
}
</ShowController>
);
export default OrderShow;

Resources