I'm using a Sider component with a Menu inside which has some Submenus on it.
When my component is static, meaning not plugged to redux, just a plain component, the Menu /> and its children work as expected.
But, in my case im trying to make my menu dynamic, so in reality my component is connected to the store using Redux and to generate all the links im using mapStateToProps and then 'connect'.
When i'm using my component with Redux, for some reason the menu doesnt work as expected. The problem boils down to ANTD not removing a class, ant-menu-inline-collapsed which basically makes my the menu not show any text even when it is expanded.
To compare current behaviours vs expected, I found the ONE difference generated on the code which is basically creating the problem
Current
Closed sider:
<div class="ant-layout-sider ant-layout-sider-collapsed" style="...">
<div class="ant-layout-sider-children">
<ul class="ant-menu ant-menu-dark ant-menu-inline-collapsed ant-menu-root ant-menu-vertical" role="menu" ...>
Opened:
<div class="ant-layout ant-layout-has-sider"><div class="ant-layout-sider" style="...">
<div class="ant-layout-sider-children">
<ul class="ant-menu ant-menu-dark **ant-menu-inline-collapsed** ant-menu-root ant-menu-vertical" role="menu">
The problem here is that the the sider is opened, the menu UL is keeping the ant-menu-inline-collapsed class. In reality it should switch it to ant-menu-inline.
I cant understand why this is happening. As soon as i remove the redux logic from my component, the Menu behaviour works fine and the class in question is removed.
The moment i put the redux logic back in, for some reason ANTD doesnt remove said class.
I'm lost as to how to proceed.
Menu configuration is the standard and it should not be affected at all as far as i know.
This is my menu code, in case you want to have a quick look:
<Menu
onClick={this.handleClick}
theme="dark"
mode="inline"
defaultSelectedKeys={['1']}
selectedKeys={[this.state.current]}>
<Menu.Item key={ROUTE_DASHBOARD}>
<Tooltip placement="right">
<Link to={ROUTE_DASHBOARD}>
<Icon type="calendar" />
<span className="nav-text">Home</span>
</Link>
</Tooltip>
</Menu.Item>
<SubMenu
key='sub2'
disabled={!budgetView}
title={<span><Icon type="mail" /><span>Top Down</span></span>}>
<Menu.Item key="3">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/exec`}>
<Icon type="calendar" />
<span className="nav-text">Exec Recap</span>
</Link>
</Menu.Item>
<Menu.Item key="4">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/total`}>
<Icon type="calendar" />
<span className="nav-text">Total</span>
</Link>
</Menu.Item>
<Menu.Item key="5">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/women`}>
<Icon type="calendar" />
<span className="nav-text">Women</span>
</Link>
</Menu.Item>
<Menu.Item key="6">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/men`}>
<Icon type="calendar" />
<span className="nav-text">Men</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="sub7"
title={<span><Icon type="team" /><span>Middle Out</span></span>}>
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</SubMenu>
<SubMenu
key="sub3"
title={<span><Icon type="file" /><span>Bottom Up</span></span>}
>
<Menu.Item key="3">Buttom Up 1</Menu.Item>
<Menu.Item key="4">Buttom Up 2</Menu.Item>
<Menu.Item key="5">Buttom Up 3</Menu.Item>
</SubMenu>
<SubMenu
key="sub4"
title={<span><Icon type="area-chart" /><span>Reporting</span></span>}>
<Menu.Item key="3">Reporting-1</Menu.Item>
<Menu.Item key="4">Reporting-2</Menu.Item>
<Menu.Item key="5">Reporting-3</Menu.Item>
</SubMenu>
</Menu>
Related
What I want:
I want to open (expand) the submenu of a specific menu when I change/update the defaultOpenKeys value.
What I've tried:
const [submenu, setSubmenu] = useState([]);
useEffect(() => {
setSubmenu(["sub1"]);
}, []);
...
<Menu style={{ width: 256 }} defaultSelectedKeys={["4"]} defaultOpenKeys={submenu} mode="inline">
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<SubMenu key="sub1" title="Submenu">
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</SubMenu>
</Menu>;
In here, The submenu is an empty array at first, but whenever the page loads, useEffect does update the value of submenu and set to the ["sub1"]. What I believe is, whenever the state values do change, the page should re-render, so <Menu defaultOpenKeys={['sub1']} ...> should update like this, right?
But in here, if I pass <Menu defaultOpenKeys=['sub1'] ... > at first then this works as I expected, but If I pass the defaultOpenKeys value from the state, then this does not work as I expected (if the initial value of state is ['sub1'] then it also works fine).
Here is the code sandbox, where I've included both working and not working demo.
CODESANDBOX
Here is the screenshot:
UPDATED:
After #HDM91 answer,
I've tried with another prop provided by antd, which is openKeys and it does open the submenu at first, but was unable to close it, or even it does block other submenus from opening/closing.
<Menu
style={{ width: 256 }}
defaultSelectedKeys={["4"]}
openKeys={submenu} // blocks other submenu by closing and opening
mode="inline"
>
I've just tried with openKeys and and handle onOpenChange to set new openkeys into submenu state it's fine:
const [submenu, setSubmenu] = useState([]);
useEffect(() => {
setSubmenu(["sub1"]);
}, []);
return (
<Menu
style={{ width: 256 }}
defaultSelectedKeys={["4"]}
defaultOpenKeys={submenu}
openKeys={submenu}
onOpenChange={(openKeys) => {
setSubmenu(openKeys);
}}
mode="inline"
>
<Menu.Item key="1">Option 1</Menu.Item>
<Menu.Item key="2">Option 2</Menu.Item>
<SubMenu key="sub1" title="Submenu">
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</SubMenu>
</Menu>
);
I use React + Typescript + ANT Design
I am making a menu in which one of the items is responsible for import, but in this case the menu item does not look correct:
Here is the code:
<Menu.ItemGroup title={"Actions"}>
<Menu.Item onClick={}><Icon type="save"/>{"Save"}</Menu.Item>
<Menu.Item onClick={}><Icon type="delete"/>{"Delete"}</Menu.Item>
<Upload customRequest={} showUploadList={false} accept=".txt">
<Menu.Item onClick={}><Icon type="import"/>{"Import"}</Menu.Item>
</Upload>
<Menu.Item onClick={}><Icon type="export"/>{"Export"}</Menu.Item>
</Menu.ItemGroup>
I would try switching the order of usage at <Upload> and <Menu.Item> components:
<Menu.ItemGroup title={"Actions"}>
<Menu.Item onClick={}><Icon type="save">{"Save"}</Menu.Item>
<Menu.Item onClick={}><Icon type="delete">{"Delete"}</Menu.Item>
<Menu.Item onClick={}><Icon type="import">
<Upload customRequest={} showUploadList={false} accept=".txt">
{"Import"}
</Upload>
</Menu.Item>
<Menu.Item onClick={}><Icon type="export">{"Export"}</Menu.Item>
</Menu.ItemGroup>
I've stacked with some strange issue. When I create a menu block with a button inside of it, I'm expecting to get a "Regular" button, but getting, kind of, Menu Item View.
Instead of
<Menu inverted>
<Menu.Menu>
<Menu.Item header>
<Image className="logo" src={logoImg} avatar />
</Menu.Item>
</Menu.Menu>
<Menu.Menu position={'right'}>
<Menu.Item>
<Button positive>Sign up</Button>
</Menu.Item>
</Menu.Menu>
</Menu>
What could be wrong?
Your code working fine. Here is the working codepen. Issue may be some other custom styles will override your component Button styles.
I have main and common top Navigation Bar (TopMenu) and a body that corresponds to different modules.
/* App.js */
render() {
if (this.props.appLoading) return <Container />;
return (
<Container className="App" fluid>
<TopMenu menuStyle={menuStyle} />
<BodyContainer />
</Container>
);
}
Each module has a bunch of menu items that need to be displayed on the common Top navigation when that module loads.
/* TopMenu.js */
render() {
return (
<Menu
fixed="top"
inverted
secondary
stackable
style={this.props.menuStyle}
>
<Menu.Item as={Link} to="/" header>
<Logo />
</Menu.Item>
{ /* Module 1 Menu Items */ }
{this.props.isAuthenticated && this.props.module==='market' && (
<Menu.Menu position="left">
<Menu.Item style={{ width: '30rem' }}>
<AutoCompleteContainer />
</Menu.Item>
<Menu.Item>
<Icon name="empty star" />
</Menu.Item>
<Menu.Item>
<Icon name="line chart" />
</Menu.Item>
<Menu.Item>
<Icon name="remove bookmark" />
</Menu.Item>
<Menu.Item>
<Icon name="lock" />
</Menu.Item>
</Menu.Menu>
)}
{ /* Module 2 Menu Items */ }
{ /* Module x Menu Items */ }
<Menu.Menu position="right">
{!this.props.isAuthenticated && notAuthenticatedMenuItems}
{this.props.isAuthenticated && (
<Menu.Item>
<ProfileDropdown onSignOut={this.props.onLogOut} />
</Menu.Item>
)}
</Menu.Menu>
</Menu>
);
}
}
Clearly, having a common top navigation is making it very difficult to have all the show/hide menu items related to each module in one place. This is because there will be lot of code in one file and also, the navigation will hold the logic for showing/displaying items related to each module, which is against best practices.
My question, is how can I structure the project/code, so that I reuse the common top navigation while each module loads the required menu items when that module loads? Is there a "Extension Point" concept in React where the common top navigation bar provides an extension point for the modules to plug-in their respective items.
You could make the top generic menu as a component and then pass your respective seperate menu items as children to this Navigation component
class TopMenu extends React.Component {
render() {
return (
<Menu
fixed="top"
inverted
secondary
stackable
style={this.props.menuStyle}
>
<Menu.Item as={Link} to="/" header>
<Logo />
</Menu.Item>
{this.props.children}
<Menu.Menu position="right">
{!this.props.isAuthenticated && notAuthenticatedMenuItems}
{this.props.isAuthenticated && (
<Menu.Item>
<ProfileDropdown onSignOut={this.props.onLogOut} />
</Menu.Item>
)}
</Menu.Menu>
</Menu>
);
}
}
}
and use it as
<Container className="App" fluid>
<TopMenu menuStyle={menuStyle}>
{ /* Module 1 Menu Items */ }
{this.props.isAuthenticated && this.props.module==='market' && (
<Menu.Menu position="left">
<Menu.Item style={{ width: '30rem' }}>
<AutoCompleteContainer />
</Menu.Item>
<Menu.Item>
<Icon name="empty star" />
</Menu.Item>
<Menu.Item>
<Icon name="line chart" />
</Menu.Item>
<Menu.Item>
<Icon name="remove bookmark" />
</Menu.Item>
<Menu.Item>
<Icon name="lock" />
</Menu.Item>
</Menu
</TopMenu>
<BodyContainer />
</Container>
Similarly you could use it at other places as well
I'm using React Bootstrap and React Router Bootstrap for my Navbar, and I am making a user profile dropdown menu list.
I'd like to be able to have an user's avatar show up in place of the 'title' property. (The same idea as the user profile dropdown on Github)
Is this possible? I don't see any options to use an image instead of title for NavDropdown
<Navbar inverse>
<Navbar.Header>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav pullRight>
<NavDropdown eventKey={ 3 } id="profile-dropdown" >
<LinkContainer to="/profile/edit" >
<NavItem eventKey={ 3.4 } > Edit </NavItem>
</LinkContainer>
<LinkContainer to="/logout">
<Logout eventKey={ 3.5 } />
</LinkContainer>
</NavDropdown>
</Nav>
</Navbar.Collapse>
</Navbar>
Would a SplitButton or straight Dropdown be a better option? I don't really see much that the "NavDropdown" is adding to the HTML.
The NavDropdown's title property can take any React element as a value. This is what I did:
<Nav pullRight>
<NavDropdown eventKey={1}
title={
<div className="pull-left">
<img className="thumbnail-image"
src={src}
alt="user pic"
/>
{user.username}
</div>
}
id="basic-nav-dropdown">
<MenuItem eventKey={1.1} href="/profile">Profile</MenuItem>
<MenuItem divider />
<MenuItem eventKey={1.3}>
<i className="fa fa-sign-out"></i> Logout
</MenuItem>
</NavDropdown>
</Nav>
You'll probably need to adjust the css a little bit.
You can also use the <Image /> component, e.g.
const UserMenu = (
<Image
src={'https://github.com/mshaaban0.png'}
alt="UserName profile image"
roundedCircle
style={{ width: '40px' }}
/>
)
<NavDropdown id="nav-dropdown-dark-example" title={UserMenu}>
//....
</NavDropdown>