I have AppBar component with a menu. Every time I click on any of the links, navigation happens, but the menu doesn't close.
import MenuItem from '#material-ui/core/MenuItem';
import Menu from '#material-ui/core/Menu';
import { Button } from '#material-ui/core';
import * as ROUTES from '../../constants/routes';
class AppBar extends React.Component {
state = {
anchorEl: null
};
handleCommonMenuOpen = event => {
this.setState({ anchorEl: event.currentTarget });
}
handleCommonMenuClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { anchorEl } = this.state;
const { classes } = this.props;
const isMenuOpen = Boolean(anchorEl);
const renderCommonMenu = (<Menu
anchorEl={anchorEl}
id="tutorials-menu"
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
open={isMenuOpen}
onClose={this.handleCommonMenuClose}
>
<MenuItem onClick={this.handleCommonMenuClose} component={Link} to={ROUTES.AUTHENTICATION_DOC}>Authentication</MenuItem>
<MenuItem onClick={this.handleCommonMenuClose} component={Link} to={ROUTES.REALTIMEDB_DOC}>Realtime DB</MenuItem>
<MenuItem onClick={this.handleCommonMenuClose} component={Link} to={ROUTES.HOSTING_DOC}>Hosting</MenuItem>
</Menu>)
return(
<div>
<Button aria-haspopup="true" aria-controls="tutorials-menu" onClick={this.handleCommonMenuOpen} color="inherit">Tutorials</Button>
{renderCommonMenu}
<Route path={ROUTES.AUTHENTICATION_DOC} component={AuthenticationDoc} />
<Route path={ROUTES.REALTIMEDB_DOC} component={ReattimeDBDoc} />
</div>)
}
}
export default AppBar;
If I click the handleCommonMenuClose, it will result in an error.
"cannot convert undefined or null to object"
Could anybody help me out to resolve this issue, please?
Related
I followed the material UI snack bar for a simple snackbar example. But instead of having a separate button to display snackbar message I want the message to appear when my existing button is clicked.
SnackBarTest.tsx --> ref Reference Link
import React from 'react';
import Button from '#material-ui/core/Button';
import Snackbar from '#material-ui/core/Snackbar';
import IconButton from '#material-ui/core/IconButton';
import CloseIcon from '#material-ui/icons/Close';
export default function SnackBarTest() {
const [open, setOpen] = React.useState(false);
const handleClick = () => {
setOpen(true);
};
const handleClose = (event: React.SyntheticEvent | React.MouseEvent, reason?: string) => {
if (reason === 'clickaway') {
return;
}
setOpen(false);
};
return (
<div>
<Button onClick={handleClick}>Open simple snackbar</Button>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={open}
autoHideDuration={6000}
onClose={handleClose}
message="Note archived"
action={
<React.Fragment>
<Button color="secondary" size="small" onClick={handleClose}>
UNDO
</Button>
<IconButton size="small" aria-label="close" color="inherit" onClick={handleClose}>
<CloseIcon fontSize="small" />
</IconButton>
</React.Fragment>
}
/>
</div>
);
}
Now inside my sample.tsx file
import SnackBarTest from './SnackBarTest';
class Sample extends React.Component {
submitButton(){
//doing some work
}
render(){
return(
<div>
<Button onClick={this.submitButton}>Submit<Button/>
<SnackBarTest />
</div>
)
}
Inside my sample.tsx It will show my existing button and second button for the snack bar. How am I able to move the SnackBarTest button functionality into my existing Button?
i want to use components(LIke: DropDown menu) in Class Component in Material Ui. i am using material-UI
and i am using class Component for.but get hooks error. i am using typescript with material UI. but i want to use Component. But I cant figure out how to do this.i am using typescript with material UI.
import React, { Component } from 'react'
import ApiService from "../../service/ApiService";
import DeleteIcon from '#material-ui/icons/Delete';
import Typography from '#material-ui/core/Typography';
import { Grid } from '#material-ui/core';
import Button from '#material-ui/core/Button';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
class CategoryListUserComponent extends Component<any,any>{
constructor(props: any){
super(props)
this.state = {
users: [],
message: null
}
this.deleteUser = this.deleteUser.bind(this);
this.editUser = this.editUser.bind(this);
this.addUser = this.addUser.bind(this);
this.reloadUserList = this.reloadUserList.bind(this);
}
render() {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<Grid>
<Typography variant="h4" align="left" style={style}>Category List</Typography>
<Button variant="contained" color="primary" onClick={() => this.addUser()}>
Add Category
</Button>
<Grid container className="listContainer">
<div>
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
Open Menu
</Button>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
</div>
</Grid>
</Grid>
);
}
}
const style ={
display: 'flex',
justifyContent: 'center'
}
export default CategoryListUserComponent;
I created a Snackbar component.
import React, { Component } from 'react';
import { Snackbar, IconButton } from '#material-ui/core';
import CloseIcon from '#material-ui/icons/Close';
interface AlertProps {
message: string;
}
interface AlertState {
open: boolean;
}
export default class Alert extends Component<AlertProps, AlertState> {
constructor(props: AlertProps) {
super(props);
this.state = {
open: true
};
this.handleClose = this.handleClose.bind(this);
}
handleClose(event: React.SyntheticEvent | React.MouseEvent, reason?: string) {
if (reason !== 'clickaway') {
this.setState({
open: false
});
}
}
render() {
return (
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={this.state.open}
autoHideDuration={6000}
onClose={this.handleClose}
message={this.props.message}
action={
<IconButton
key="close"
color="inherit"
onClick={this.handleClose}
>
<CloseIcon />
</IconButton>
}
/>
)
}
}
I then programmatically add it to a render when an error is encountered while submitting a form.
let alert: ReactNode;
if (this.state.error) {
alert = <Alert message={this.state.error} />;
}
Problem is the Snackbar only opens the first time an error is encountered. If a user submits the same form twice, the Snackbar doesn’t open.
I know it’s because of this.state.open = false which is set by the onClose method, but how can I "reset" this state before the form is submitted again?
One way is you could change your approach a bit and always have Alert rendered, i.e.
<Alert message={this.state.error} open={this.state.open} onClose={()=>{this.setState({open:false})}}/>
Also move the open state variable from Alert's state to its parent. So in Alert use the open value from props always. Now, whenever open is changed in the parent, Alert will re render properly.
I am using social login in react-admin (former admin-on-rest) and I have the user picture from his social media, however I didn't find how to change the user profile image in the top right corner of the screen:
Is there any prop to be set, like custom login or custom logout button?
Thanks.
Currently, the process involves a lot of code as you'll have to rewrite the UserMenu completely. To use it, you'll also have to implement a custom Layout with a custom AppBar. The process will be simplified when https://github.com/marmelab/react-admin/pull/2391 will be merged.
// in src/MyUserMenu.js
import React, { Children, cloneElement } from 'react';
import PropTypes from 'prop-types';
import Tooltip from '#material-ui/core/Tooltip';
import IconButton from '#material-ui/core/IconButton';
import Menu from '#material-ui/core/Menu';
import AccountCircle from '#material-ui/icons/AccountCircle';
import { translate } from 'ra-core';
class UserMenu extends React.Component {
static propTypes = {
children: PropTypes.node,
label: PropTypes.string.isRequired,
logout: PropTypes.node,
translate: PropTypes.func.isRequired,
};
static defaultProps = {
label: 'ra.auth.user_menu',
};
state = {
auth: true,
anchorEl: null,
};
handleChange = (event, checked) => {
this.setState({ auth: checked });
};
handleMenu = event => {
this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({ anchorEl: null });
};
render() {
const { children, label, logout, translate } = this.props;
if (!logout && !children) return null;
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
return (
<div>
<Tooltip title={label && translate(label, { _: label })}>
<IconButton
arial-label={label && translate(label, { _: label })}
aria-owns={open ? 'menu-appbar' : null}
aria-haspopup="true"
onClick={this.handleMenu}
color="inherit"
>
{/* Replace this icon with whatever you want, a user avatar or another icon */}
<AccountCircle />
</IconButton>
</Tooltip>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={open}
onClose={this.handleClose}
>
{Children.map(children, menuItem =>
cloneElement(menuItem, { onClick: this.handleClose })
)}
{logout}
</Menu>
</div>
);
}
}
export default translate(UserMenu);
// in src/MyAppBar.js
import { AppBar } from 'react-admin';
import MyUserMenu from './MyUserMenu';
const MyAppBar = (props) => <AppBar {...props} userMenu={MyUserMenu} />;
// in src/MyLayout.js
import { Layout } from 'react-admin';
import MyAppBar from './MyAppBar';
const MyLayout = (props) => <Layout {...props} appBar={MyAppBar} />;
export default MyLayout;
Documentation: https://marmelab.com/react-admin/Theming.html#using-a-custom-appbar
I can mention that UP and DOWN arrow keys move the selection, which makes me believe it's tabbed into somehow.
There is a material-ui Menu component, that has Gatsby Link components wrapping the MenuItems components. I removed non-relevant code.
class NavBarDesktop extends React.Component {
state = {
anchorEl: null,
}
handleClick = event => {
this.setState({ anchorEl: event.currentTarget })
}
handleClose = () => {
this.setState({ anchorEl: null })
}
render() {
const { anchorEl } = this.state
return (
<div>
<Button
aria-owns={anchorEl ? 'fade-menu' : null}
onClick={this.handleClick}
className={classes.button}
>
Categories
</Button>
<Menu
id="fade-menu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={this.handleClose}
>
<Link to={'accessories'}>
<MenuItem onClick={this.handleClose}>Accessories</MenuItem>
</Link>
<Link to={'automotive'}>
<MenuItem onClick={this.handleClose}>Automotive</MenuItem>
</Link>
<Link to={'electronics'}>
<MenuItem onClick={this.handleClose}>Electronics</MenuItem>
</Link>
</Menu>
</div>
)
}
}
NavBarDesktop.propTypes = {
classes: PropTypes.object.isRequired,
}
export default withStyles(styles)(NavBarDesktop)
Someone encountered this before?
I don't really understand your question, but if you expect to get the behaviour of material-ui, I recommend you to write your navigation button as recommended in the Third-party routing library doc.
I use this in my projects:
// ./src/components/LinkMenuItem.js
import React from 'react';
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
import MenuItem from '#material-ui/core/MenuItem';
const LinkMenuItem = ({ to, children, ...rest }) => (
<MenuItem component={Link} to={to} {...rest}>
{children}
</MenuItem>
);
LinkMenuItem.propTypes = {
to: PropTypes.string.isRequired,
children: PropTypes.string.isRequired,
};
export default LinkMenuItem;
Then in your NavBarDesktop component:
import LinkMenuItem from './path/to/component';
// ...
<Menu
id="fade-menu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={this.handleClose}
>
<LinkMenuItem onClick={this.handleClose} to='/accessories'>Accessories</LinkMenuItem>
<LinkMenuItem onClick={this.handleClose} to='/automotive'>Automotive</LinkMenuItem>
<LinkMenuItem onClick={this.handleClose} to='/electronics'>Electronics</LinkMenuItem>
</Menu>