How to display React-Bootstrap's NavDropdown on Hover - reactjs

Related to this question, whose top answers no longer work. By default, the NavDropdown only appears when clicked on, however I need this dropdown to display on hover. I struggled loading 'React-Bootstrap' into stackoverflow to create a reproducible example, however here is a basic Navbar using react-bootstrap, that features my attempt to display the dropdown on hover:
const [isStatsOpen, setIsStatsOpen] = useState(true);
<Navbar>
<Navbar.Brand>
<Link to='/'>
<img alt='company logo' src={My Logo} />
</Link>
</Navbar.Brand>
<Navbar.Toggle aria-controls='basic-navbar-nav' />
<Navbar.Collapse id='basic-navbar-nav'>
<Nav className='mr-auto'>
<NavDropdown title='Statistics'
onMouseEnter={() => setIsStatsOpen(true)}
onMouseLeave={() => setIsStatsOpen(false)}
open={isStatsOpen}
>
<NavDropdown.Item as={Link} to='/stats/'> Stats 1</NavDropdown.Item>
<NavDropdown.Item as={Link} to='/stats2/'>Stats 2</NavDropdown.Item>
</NavDropdown>
</Nav>
<Nav className='ml-auto'>
<DivisionSelect />
<AppSelect />
</Nav>
</Navbar.Collapse >
</Navbar >
From the linked post above, there were 2 responses with 10+ votes, however neither of these solutions work. As it pointed out in one of the comments: This doesn't work in newer versions, the dropdown isn't rendered until it's first click. You'd need to trigger the onclick before you could control via css.
After inspecting the page, I can confirm that this person is correct - there is no menu for which to display until after the NavDropdown has been clicked upon. Once clicked, the menu is there, and then the solutions from this other post do work. Given this as the case, how can I resolve this issue? Is it possible for my react component to automatically "click" the Navdropdowns on load, that way the menus will appear on hover?
Thanks!

Does this help you? Old good vanilla javascript.
I added an id at NavDropdown and called the old, classic document.getElementById method and then triggered the click event.
useEffect(() => {
document.getElementById("test").click();
}, []);
<NavDropdown
id="test"
title="Statistics"
https://codesandbox.io/s/react-bootstrap-demo-z36c5

In this link to the earlier version of the question, the highly voted answer that starts with export class Nav extends React.Component { does work, so long as the open prop is updated to show.

Related

Warning: validateDOMNesting(…): <a> cannot appear as a descendant of <a>

I'm using React router dom Link component. It is basically twitter's home feed. I want to be able to have two type of Links in one div component. One will be Link to go to user's profile and other one to go to post. I am currently getting warning and couldn't find solution for now. Here is the screenshot as reference:
I understand the issue here, my post Link is the parent element and I've added two user Link components inside of it as the user should be able to access post page when he clicks on anything inside of the post except user's profile photo and user's name. Is there any smarter way of achieving this and keeping links like this?
Code:
{posts?.map((post) => (
<Link
className={classes.link}
key={post.user.id}
to={`/posts/${post.id}`}
>
<div className={classes.post}>
<Link to={`/users/${post.user.id}`}>
<img
className={classes.profileImage}
src={DefaultLogo}
alt="default-profile"
/>
</Link>
<article className={classes.postDetails}>
<Link
className={classes.link}
to={`/users/${post.user.id}`}
>
<Typography
variant="subtitle1"
className={`${classes.postTitle} ${classes.content}`}
>
{post.user.name}
</Typography>
</Link>
<Typography
variant="subtitle1"
className={`${classes.postText} ${classes.content}`}
>
{post.body}
</Typography>
</article>
</div>
</Link>
))}
Yes, having anchor tags inside of another anchor tag is misleading a bad approach to doing things. But given your requirements you can make use of a basic button with react router dom history api.
A simple example:
import {Link, useHistory} from 'react-router-dom'
const App = () => {
const history = useHistory()
return (
<a
href='/user/1'
>
<h2>John doe</h2>
<div>here are some use information</div>
{/* Need to prevent the event bubbling */}
<Link role='link' to='/users/1/posts'>
User posts
</Link>
</div>
)
}

Reactstrap generic flex directives

I'm using Reactstrap to style my React/Redux app, and I'm having some trouble aligning things as I'd like; I know I'm giving up easy access to finer-grained control using the library, but I suspect there's a way to do what I want.
So, I have a basic Navbar at the top of my content (I compressed the Nav options).
<Navbar light expand={ "lg" }>
<NavbarBrand>Redacted Name</NavbarBrand>
<NavbarToggler onClick={ () => this.setState({ isOpen: !this.state.isOpen }) } />
<Collapse navbar isOpen={ this.state.isOpen }>
<Nav navbar>
<UncontrolledDropdown className={ "mr-lg-2" } nav inNavbar>
<DropdownToggle nav>
About Us
</DropdownToggle>
<DropdownMenu>
<DropdownItem>
<Link to={ "/" }>Mission Statement</Link>
</DropdownItem>
</DropdownMenu>
</UncontrolledDropdown>
</Nav>
{ userOptions }
</Collapse>
</Navbar>
Now, userOptions is a form that changes based on user input, but all... forms of it take this basic inline form layout, save for differences in buttons and links inside.
<Form className={ "userStatusForm" } inline >
// Content
</Form>
Here's my issue: in the simple layout I'm using, the brand, toggle, nav, and user form pile against each other on the left. However, I'm wanting content inside the Collapse element to use a standard "justify content between" alignment to press the user form against the right side, regardless of its size. That said, I'm struggling to find what I need in the Reactstrap docs - and to phrase what I'm trying to do correctly for Google.
So, I was trying to avoid direct className assignments, since I've already decided to use the Reactstrap library, but the simplest thing involved a mr-auto class added on to the Nav component that forms the first item inside the Collapse container. Not as elegant as I'd hoped, but it's simple (and was good enough for the Navbar in the Reactstrap docs).

How to intercept and possibly cancel onSelect in React Bootstrap?

I am very new to React Bootstrap and exploring how much it is customizable. While it's easy to hook into onSelect, I can't find any way to cancel the action itself. For example, if user has unsaved changes and should not go to the other route, then how onSelect can be prevented?
This is from React Bootstrap docs with a little change:
import React from 'react';
import {Navbar, Nav, NavItem, NavDropdown, MenuItem} from 'react-bootstrap';
// import './Header.scss';
function myOnSelect(eventKey, event) {
console.log(eventKey);
console.log(event);
alert(`Going ${eventKey}`);
return false // How to prevent from going there???
}
export const Header = props => {
const {brand} = props.topNav;
return (
<Navbar collapseOnSelect expand="sm" bg="light" variant="light" sticky="top"
onSelect={myOnSelect}>
<Navbar.Brand href="#home">{brand}</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav"/>
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="#features">Features</Nav.Link>
<Nav.Link href="#pricing">Pricing</Nav.Link>
<NavDropdown title="Dropdown" id="collasible-nav-dropdown">
<NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
<NavDropdown.Item href="#action/3.2">Another action</NavDropdown.Item>
<NavDropdown.Item href="#action/3.3">Something</NavDropdown.Item>
<NavDropdown.Divider/>
<NavDropdown.Item href="#action/3.4">Separated link</NavDropdown.Item>
</NavDropdown>
</Nav>
<Nav>
<Nav.Link href="#deets">More deets</Nav.Link>
<Nav.Link eventKey={2} href="#memes">
Dank memes
</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
);
};
Note: I do not mean going away to completely another page via a link, just another route inside the same page. It can be for "unsaved changes" or some other temporary reason, so disabling navbar menus is an overkill from UX point of view.
Extra twist to this is the project is going to use react-observable (with Redux and RxJS) for event/action logic, so it would be nice to understand how easy it is to hook React Boostrap into it. That is, some actions will need to be intercepted and handled in the epics and other middleware instead of following default React Bootstrap automation. So:
The example here can have some very specific solution, but could be nice to also hear how easy it is to do action-interceptions in other places of React Bootstrap or whether the javascript logic should be heavily customized or even abandoned.
I guess, for Navs the answer is somewhere in these arrangements: https://github.com/react-bootstrap/react-bootstrap/blob/master/src/AbstractNavItem.js
Maybe explicitly set activeKey property together with event.preventDefault(); can be used to build the desired control over nav.
We cannot stop the select from performing its onchange property. It will execute as soon as we select an option. Consider adding conditions in the myOnSelect function
function myOnSelect(eventKey, event) {
console.log(eventKey);
console.log(event);
if(condition) {
//do something
alert(`Going ${eventKey}`);
}
else {
//do something else
return false // How to prevent from going there???
}
}

Whats the best way to handle Nav item selection?

I'm using React + Redux for one of my applications. Where I want to have a Nav, with 2 Nav Items. I want to handle the change of Nav item based on the user action.
I'm planning to use my redux store to have a state for this to handle this change. Is this a good approach or please guide me for a better solution. I'm a beginner in this.
My Nav will look something like this
<Nav {...props} activeKey={active} onSelect={onSelect} style={styles} appearance="subtle" justified>
<Nav.Item eventKey="nav-1">
Nav 1
</Nav.Item>
<Nav.Item eventKey="nav-2" >
Nav 2
</Nav.Item>
</Nav>
(using rsuite js for this)
My initial state of the store will be something like this
const initialState = {
selectedNav: 'nav-1'
};
yes I think you are on the right way, but Nav 1 and Nav 2 does not have to know about the state the parent Component does
pass the redux state as props to the parent Nav and then render the selected Nav from the element props.
<Nav onSelect={onSelect} style={styles} appearance="subtle" justified>
{props.selectedNav === 'nav-1' && (
<Nav.Item eventKey="nav-1">
Nav 1
</Nav.Item>
)}
{props.selectedNav === 'nav-2' && (
<Nav.Item eventKey="nav-2" >
Nav 2
</Nav.Item>
)}
</Nav>
and make a global action for the user interaction to change the active nave.
Note:
you can take another approach for more cleaner code by wrapping the Nav 1 and Nav 2 into a switch component and render the active one.

EventKeys in NavDropdown in React-Bootstrap

I have a problem with the eventKey thing in NavDropdowns.
var Navigation = React.createClass({
handleSelect: function(eventKey){
console.log(eventKey);
},
render: function() {
return (
<Navbar brand='Navbar' toggleNavKey={0}>
<CollapsibleNav eventKey={0} onSelect={this.handleSelect}>
<Nav navbar>
<NavItem eventKey={1}>Home</NavItem>
</Nav>
<Nav navbar right hide>
<NavItem eventKey={2}>Login</NavItem>
<NavDropdown eventKey={3} title='NavDropdown' id='basic-nav-dropdown'>
<MenuItem eventKey={4}>Action 1</MenuItem>
<MenuItem eventKey={5}>Action 2</MenuItem>
</NavDropdown>
</Nav>
</CollapsibleNav>
</Navbar>
)
}
});
I want to be able in my selectHandler to tell what Nav element was clicked.
This works great for all elements except the NavDropdown:
Clicking the Dropdown does not trigger the selectHandler, which is fine.
But when I click one of the MenuItem, instead of giving me the eventKey, it gives me an event object.
How can I modify the NavDropdown so that it gives me the eventKey?
Edit: My versions are:
"react": "^0.14.0-beta3",
"react-bootstrap": "^0.25.100-react-pre.0",
It is a bug in react-bootstrap
https://github.com/react-bootstrap/react-bootstrap/issues/1268
The callback on onSelect event will receive 2 params. The first one is event obj. The second is EventKey. You can read it in doc. So if you want to get event key, you should try to call it in the second param
handleSelect: function(event,eventKey){
console.log(event)
console.log(eventKey);
},

Resources