can't use PopOver in Header Menu Item - javascript

i want create a header with popover component
import React from "react";
import { Layout, Menu, Button } from "antd";
const { Header, Popover } = Layout;
const { SubMenu } = Menu;
const Index = (props) => {
const content = (
<menu>
<Menu.Item>Menu</Menu.Item>
<SubMenu title="SubMenu">
<Menu.Item>SubMenuItem</Menu.Item>
</SubMenu>
</menu>
);
return (
<Header>
<div
className=" float-left text-light"
style={{ fontSize: "1.4rem" }}
>
Shonode
</div>
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["2"]}
style={{ lineHeight: "64px" }}
>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
<Menu.Item key="3">sda</Menu.Item>
</Menu>
</Header>
);
};
export default Index;
this component work but when i paste item 3 with
<Menu.Item key="3">
<Popover content={content}title="Title" trigger="hover">
<Button>Hover me</Button>
</Popover>
</Menu.Item>
i get error
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

the problem is that you are trying to import Popover from Layout which is not a component of Layout
instead of:
import { Layout, Menu, Button } from "antd";
const { Header, Popover } = Layout;
try:
import { Popover, Layout, Menu, Button } from "antd";
const { Header } = Layout;

Related

Ant Design Sidebar Layout Example Not working as expected

I have a React webapp with Ant design component framework version 4.
I tried to use this example from Antd docs in my webapp:
https://codesandbox.io/s/ymspt
But I don't get the same result when I use the code in my webapp.
This is how it looks like in my app
import { MenuFoldOutlined, MenuUnfoldOutlined, UploadOutlined, UserOutlined, VideoCameraAddOutlined } from "#ant-design/icons";
import { Layout, Menu, Space } from "antd";
import { Content, Footer, Header } from "antd/lib/layout/layout";
import Sider from "antd/lib/layout/Sider";
import React, { useState } from "react";
import { logOut } from "../firebase";
function Lineups() {
const [collapsed, setCollapsed] = useState(false);
const [logoText, setLogoText] = useState("TEAMTAC");
const toggle = () => {
setCollapsed(!collapsed);
collapsed ? setLogoText("TEAMTAC") : setLogoText("TT")
};
const signOutFirebase = async () => {
await logOut();
}
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider collapsible collapsed={collapsed} onCollapse={toggle}>
<h2 style={{textAlign:"center", marginTop: 15}}>{logoText}</h2>
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1" icon={<UserOutlined />}>
nav 1
</Menu.Item>
<Menu.Item key="2" icon={<VideoCameraAddOutlined />}>
nav 2
</Menu.Item>
<Menu.Item key="3" icon={<UploadOutlined />}>
nav 3
</Menu.Item>
</Menu>
</Sider>
<Layout className="site-layout">
<Header style={{ padding: 5 }}>
Header
</Header>
<Content
style={{
margin: '24px 16px',
padding: 24,
minHeight: 280,
}}
>
Content
</Content>
<Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
</Layout>
</Layout>
)
}
export default Lineups;
I dont have any extra css or whatever.
How come that I don't get the same result?
Problem were the components which Ive imported.
I did it the wrong way.
So instead of
import { Content, Footer, Header } from "antd/lib/layout/layout";
I had to do
import { Layout } from "antd";
...
const { Header, Footer, Sider, Content } = Layout;

React - Why are my links being rendered below my button?

Here I have an image of the current header render. The headercomprises of a HeaderMenu and 3 Links. The links are working fine, but the HeaderMenu is causing the links to fall below it. HeaderMenu contains a div which wraps a Button and Popper, acting as a dropdown menu.
I'm also not sure if this design structure is correct, but for the styles, I have one styles.js file which I pull from. I then pass these styles down as props to the smaller components for rendering. That's why the components here have a props classes that comes from useStyles in index.js.
Header.js
import { AppBar, Button, Link, Toolbar, Typography } from '#material-ui/core'
import HeaderMenu from './HeaderMenu.js'
const Header = (props) => {
const { classes } = props
return (
<AppBar position="static" color="default" elevation={0} className={classes.appBar}>
<Toolbar className={classes.toolbar}>
<Typography variant="h6" color="inherit" noWrap className={classes.toolbarTitle}>
Company name
</Typography>
<nav>
<HeaderMenu classes={classes}/>
<Link variant="button" color="textPrimary" href="#" className={classes.link}>
Here
</Link>
<Link variant="button" color="textPrimary" href="#" className={classes.link}>
Enterprise
</Link>
<Link variant="button" color="textPrimary" href="#" className={classes.link}>
Support
</Link>
</nav>
<Button href="#" color="primary" variant="outlined" className={classes.link}>
Login
</Button>
</Toolbar>
</AppBar>
)
}
export default Header
HeaderMenu.js
import React from 'react'
import { Button, ClickAwayListener, Grow, Paper, Popper, MenuItem, MenuList } from '#material-ui/core'
const HeaderMenu = (props) => {
const { classes } = props
const [open, setOpen] = React.useState(false)
const anchorRef = React.useRef(null)
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen)
}
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return
}
setOpen(false)
}
function handleListKeyDown (event) {
if (event.key === 'Tab') {
event.preventDefault()
setOpen(false)
}
}
// return focus to the button when we transitioned from !open -> open
const prevOpen = React.useRef(open)
React.useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current.focus()
}
prevOpen.current = open
}, [open])
return (
<div className={classes.link}>
<Button
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
Details
</Button>
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
)
}
export default HeaderMenu
You should wrap all your links into a div and then add this to your <nav></nav>
display: flex;
justify-content: space-evenly;
align-items: center;
This will allow it to stay everything in the same line.

Is there a way to control where a nested Material UI select in a popper gets mounted in the DOM?

I am trying to place a select menu in a Popper. The issue I'm running into is that the nested select menu wants to mount the popup that comes out of it on the body as a neighbor and not a child of popper. This causes the clickaway event to fire. Here's the code to reproduce it:
import React, { useState } from "react";
import "./styles.css";
import Popper from "#material-ui/core/Popper";
import TextField from "#material-ui/core/TextField";
import MenuItem from "#material-ui/core/MenuItem";
import ClickAwayListener from "#material-ui/core/ClickAwayListener";
export default function App() {
const [popperAnc, setPopperAnc] = useState(null);
const popperOpen = Boolean(popperAnc);
return (
<div className="App">
<div
onClick={e => {
setPopperAnc(e.currentTarget);
}}
>
Popper anchor
</div>
<div style={{ position: "absolute" }}>
<Popper open={popperOpen} anchorEl={popperAnc}>
<ClickAwayListener
onClickAway={e => {
setPopperAnc(null);
}}
>
<TextField select label="Menu">
<MenuItem value="select1">Select me!</MenuItem>
</TextField>
</ClickAwayListener>
</Popper>
</div>
</div>
);
}
https://codesandbox.io/s/strange-bassi-liwdc?file=/src/App.js:0-1013
If you need to use Select - just use it (don't use TextField, it doesn't make any sense).
You need to make sure that the second popper is not rendered as a portal (you need to set disabledPortal on the MenuProps of the Select element.
You need to tell the new menu where to position itself and what will be it's size.
<div style={{ position: "absolute" }}>
<Popper open={popperOpen} anchorEl={popperAnc}>
<ClickAwayListener
onClickAway={e => {
console.log("click away");
setPopperAnc(null);
}}
>
<div>
<div>Wow</div>
<Select
label="Menu"
MenuProps={{
disablePortal: true,
anchorEl: this,
style: { marginTop: "20px", width: "150px", height: "200px" }
}}
>
<MenuItem value="select1">Select me!</MenuItem>
</Select>
</div>
</ClickAwayListener>
</Popper>
</div>
Here is a working example: https://codesandbox.io/s/mui-nested-popper-4uu5l?file=/src/App.js

How to make a button click another button from different components in React

I'm new to react and it is kinda hard to understand the one way data flow on it, i was making a simple app and i'm using mdbootstrap for some ready bootstrap components, I imported the component of a modal (which has a button when clicked it toggles a modal) so in my app i have cards, each one has a button that's supposed to toggle the button, but i couldn't figure out how to link the card's button with the mdbootstrap component's button.
The Card component:
import React, { Component } from 'react';
import ModalPage from './modal.jsx'
class Card extends Component {
render() {
return (
<div>
<div className="card m-5" style={{ width: '18rem' }}>
<img src={this.props.img} className="card-img-top" />
<div className="card-body">
<h5 className="card-title">{this.props.title}</h5>
<p className="card-text">{this.props.desc}</p>
<button onClick={/*I don't know what exactly i should put here */}></button>
</div>
</div>
</div>
)
}
}
export default Card;
The modal componant:
import React, { Component } from 'react';
import { MDBContainer, MDBBtn, MDBModal, MDBModalBody, MDBModalHeader, MDBModalFooter } from 'mdbreact';
class ModalPage extends Component {
state = {
modal13: false
}
toggle = nr => () => {
let modalNumber = 'modal' + nr
this.setState({
[modalNumber]: !this.state[modalNumber]
});
}
render() {
return (
<MDBContainer>
{/* This is the button I want to click when clicking the card's button */}
<MDBBtn color="primary" onClick={this.toggle(13)}>
Modal
</MDBBtn>
<MDBModal isOpen={this.state.modal13} toggle={this.toggle(13)}>
<MDBModalHeader toggle={this.toggle(13)}>
{this.props.title}
</MDBModalHeader>
<MDBModalBody>
{/* edit here */}
{this.props.content}
</MDBModalBody>
<MDBModalFooter>
<MDBBtn color="secondary" onClick={this.toggle(13)}>
Close
</MDBBtn>
<MDBBtn color="primary">Save changes</MDBBtn>
</MDBModalFooter>
</MDBModal>
</MDBContainer>
);
}
}
export default ModalPage;
Rather than having 2 click events you only need one on the child component. Instead of trying to send a click to the parent button in order to call toggle() just pass the toggle function to the child to be called:
Card:
import React, { Component } from 'react';
import ModalPage from './modal.jsx'
class Card extends Component {
render() {
return (
<div>
<div className="card m-5" style={{ width: '18rem' }}>
<img src={this.props.img} className="card-img-top" />
<div className="card-body">
<h5 className="card-title">{this.props.title}</h5>
<p className="card-text">{this.props.desc}</p>
//*****************************************
<button onClick={this.props.click}></button>
//*****************************************
</div>
</div>
</div>
)
}
}
export default Card;
Modal:
import React, { Component } from 'react';
import { MDBContainer, MDBBtn, MDBModal, MDBModalBody, MDBModalHeader, MDBModalFooter } from 'mdbreact';
class ModalPage extends Component {
state = {
modal13: false
}
toggle = nr => () => {
let modalNumber = 'modal' + nr
this.setState({
[modalNumber]: !this.state[modalNumber]
});
}
render() {
return (
<MDBContainer>
{/* I am assuming that this is a reference to <Card /> - simply pass in the onClick function as a parameter. You can even use onClick here and this.props.onClick in the child element */}
<MDBBtn color="primary" click={this.toggle(13)}>
Modal
</MDBBtn>
<MDBModal isOpen={this.state.modal13} toggle={this.toggle(13)}>
<MDBModalHeader toggle={this.toggle(13)}>
{this.props.title}
</MDBModalHeader>
<MDBModalBody>
{/* edit here */}
{this.props.content}
</MDBModalBody>
<MDBModalFooter>
<MDBBtn color="secondary" onClick={this.toggle(13)}>
Close
</MDBBtn>
<MDBBtn color="primary">Save changes</MDBBtn>
</MDBModalFooter>
</MDBModal>
</MDBContainer>
);
}
}
export default ModalPage;

ReactJs not defined

I have little problems with my ReactJs app, I'm receiving message like:
./src/components/Navbar/index.js
Line 13:3: 'state' is not defined no-undef
Line 17:3: 'handleClick' is not defined no-undef
I'm beginner with JavaScript and it's seams it's easy fix, I just copied that code from another app where it works.
This is my code:
import React, { useState } from 'react';
import { Layout, Menu, Drawer, Icon } from 'antd';
import { Mobile, Default } from '../Responsive';
import './index.less';
const { Header } = Layout;
const { SubMenu } = Menu;
const Navbar = props => {
state = {
current: 'home',
};
handleClick = e => {
console.log('click ', e);
this.setState({
current: e.key,
});
};
const [visible, setVisible] = useState(false);
const menu = (
<Menu
theme="lite"
mode="horizontal"
defaultSelectedKeys={['2']}
style={{ lineHeight: '64px' }}
onClick={this.handleClick} selectedKeys={[this.state.current]} mode="horizontal">
<Menu.Item key="mail">
<Icon type="mail" />
Navigation One
</Menu.Item>
<Menu.Item key="app" disabled>
<Icon type="appstore" />
Navigation Two
</Menu.Item>
<SubMenu
title={
<span className="submenu-title-wrapper">
<Icon type="setting" />
Navigation Three - Submenu
</span>
}
>
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup title="Item 2">
<Menu.Item key="setting:3">Option 3</Menu.Item>
<Menu.Item key="setting:4">Option 4</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
<Menu.Item key="alipay">
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Navigation Four - Link
</a>
</Menu.Item>
</Menu>
);
return (
<Header className="app-header">
<Default>{menu}</Default>
<Mobile>
<Icon type="bars" size="large" className="nav-icon" onClick={() => setVisible(true)} />
<Drawer
title=""
placement="left"
closable
onClose={() => setVisible(false)}
visible={visible}
className="nav-drawer"
>
{menu}
</Drawer>
</Mobile>
</Header>
);
};
export default Navbar;
I copied this state and handleClick from another app but it was class, I tried to change const Navbar with class NavBar extends Component but no success.
You are mixing react components and react hooks. You should either use hook (useState), or components (this).
Here is your code with hooks
import React, { useState } from 'react';
import { Layout, Menu, Drawer, Icon } from 'antd';
import { Mobile, Default } from '../Responsive';
import './index.less';
const { Header } = Layout;
const { SubMenu } = Menu;
function Navbar(props) {
const [current, setCurrent] = useState('home');
const [visible, setVisible] = useState(false);
function handleClick(e) {
console.log('click ', e);
setCurrent(e.key)
};
const menu = (
<Menu
theme="lite"
mode="horizontal"
defaultSelectedKeys={['2']}
style={{ lineHeight: '64px' }}
onClick={this.handleClick} selectedKeys={[current]} mode="horizontal">
<Menu.Item key="mail">
<Icon type="mail" />
Navigation One
</Menu.Item>
<Menu.Item key="app" disabled>
<Icon type="appstore" />
Navigation Two
</Menu.Item>
<SubMenu
title={
<span className="submenu-title-wrapper">
<Icon type="setting" />
Navigation Three - Submenu
</span>
}
>
<Menu.ItemGroup title="Item 1">
<Menu.Item key="setting:1">Option 1</Menu.Item>
<Menu.Item key="setting:2">Option 2</Menu.Item>
</Menu.ItemGroup>
<Menu.ItemGroup title="Item 2">
<Menu.Item key="setting:3">Option 3</Menu.Item>
<Menu.Item key="setting:4">Option 4</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
<Menu.Item key="alipay">
<a href="https://ant.design" target="_blank" rel="noopener noreferrer">
Navigation Four - Link
</a>
</Menu.Item>
</Menu>
);
return (
<Header className="app-header">
<Default>{menu}</Default>
<Mobile>
<Icon type="bars" size="large" className="nav-icon" onClick={() => setVisible(true)} />
<Drawer
title=""
placement="left"
closable
onClose={() => setVisible(false)}
visible={visible}
className="nav-drawer"
>
{menu}
</Drawer>
</Mobile>
</Header>
);
};
export default Navbar;
Everything else looks fine except:
const [current, setCurrent] = useState('home') ;
const handleClick = e => {
console.log('click ', e);
setCurrent(e.key);
};
Actually you have to convert the this.state to hook as well.

Categories

Resources