Currently i have the following structure
<OverlayTrigger trigger={["hover", "focus", "click"]} placement="bottom" overlay={(
<Popover className="timeline-popover-container" id="tpc-1">
<TimelinePopover
alert={session}
previousAlert={prevSession}
nextAlert={nextSession}
status={status}
/>
</Popover>
)}>
<div className="myclass">
<div>{img}</div>
</div>
</OverlayTrigger>
So, when the popover is triggered and i try to hover over the popover, the popover dissapear.
I want to be able to click inside de popover, do things inside of that and just dissapear when i move the mouse out of it.
I did a little component to handle this use case (inspired from the answer by #AnnieP).
https://gist.github.com/lou/571b7c0e7797860d6c555a9fdc0496f9
Usage:
<PopoverStickOnHover
component={<div>Holy guacamole! I'm Sticky.</div>}
placement="top"
onMouseEnter={() => { }}
delay={200}
>
<div>Show the sticky tooltip</div>
</PopoverStickOnHover>
I manage to make that work using one of the comments that ochi posted.
<OverlayTrigger trigger={["hover"]} placement="bottom" overlay={(
<Popover onMouseOver={this.showOverlay} onMouseOut={this.hideOverlay}>
content
</Popover>
)}>
<div onMouseOver={this.showOverlay} onMouseOut={this.hideOverlay}>
<div>bla bla bla</div>
</div>
</OverlayTrigger>
adding trigger on the popover and on the div i want to trigger worked.
I was looking to do the same thing using React Bootstrap except with a tooltip instead of a popover. The answer here gave me a good jumping off point, which I mixed with the Custom Overlay example in the React Bootstrap docs.
I replaced OverlayTrigger with a custom Overlay, which wraps the Tooltip but is outside of the target element, whereas the OverlayTrigger wraps the target element and calls Tooltip through the overlay prop. I added onMouseEnter() and onMouseLeave() to both the tooltip and the target element to toggle the tooltip visibility state so that leaving either one will close the tooltip.
This is a bare-bones version of my implementation:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Tooltip, Overlay } from 'react-bootstrap';
const TooltipExample extends Component {
constructor(props) {
super(props);
this.state = {
showTooltip: false,
};
}
render() {
let myTooltip = (
<Tooltip
onMouseEnter={() => this.setState({ showTaskTooltip: true })}
onMouseLeave={() => this.setState({ showTaskTooltip: false })}
>
I'm a tooltip and I'll stay open when you leave the target element and hover over me!
</Tooltip>
);
return(
<div>
<h3
ref="target"
onMouseEnter={() => this.setState({ showTooltip: true })}
onMouseLeave={() => this.setState({ showTooltip: false })}
>
Hover over me!
</h3>
<Overlay
show={this.state.showTooltip}
onHide={() => this.setState({ showTooltip: false })}
placement="bottom"
target={() => ReactDOM.findDOMNode(this.refs.target)}
>
{myTooltip}
</Overlay>
</div>
);
}
}
export default TooltipExample;
Related
So I have this code wherein there's a button in the Header.jsx file that once clicked, it will display the content of the Notification.jsx file. The problem here is, I don't exactly know how can I display the content of Notification.jsx in index.js. I tried using conditional statements but to no avail and is it possible to hide the h1 element once the button is clicked?
Header.jsx
import React from "react";
import { Button } from "#mui/material";
import { IconButton } from "#mui/material";
import NotificationsIcon from "#mui/icons-material/Notifications";
import SearchIcon from "#mui/icons-material/Search";
import Menu from "#mui/material/Menu";
import MenuItem from "#mui/material/MenuItem";
import FullNotifList from "./FullNotifList"
export default function Header() {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<Button
id="basic-button"
aria-controls={open ? "basic-menu" : undefined}
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
onClick={handleClick}
>
<IconButton>
<NotificationsIcon />
</IconButton>
</Button>
<Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
"aria-labelledby": "basic-button",
}}
>
{/* Button needs to be clicked in order to display Notification.jsx */}
<Button variant="contained">Notification Center</Button>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</Menu>
<IconButton>
<SearchIcon />
</IconButton>
</div>
);
}
Notification.jsx
import React from "react";
export default function Notification(){
return(
<div>
<ul>
<li> Hello </li>
<li> Hello </li>
<li> Hello </li>
<li> Hello </li>
</ul>
</div>
)
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
import Header from './Header'
import './index.css'
import Footer from './Footer';
import Notification from './Notification';
export default function Page(props) {
const [isClicked, setIsClicked] = React.useState(false)
function showHide(e) {
setIsClicked(true)
};
return(
<div className='main'>
<Header onClick={showHide}/>
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
)
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);
Here is the sandbox link: https://codesandbox.io/s/friendly-darkness-9u5s22?file=/src/index.js
You were right to use conditional rendering, however your conditional logic isn't working because you're assuming your Header component has an event listener.
Events listeners are an important part of Javascripts history, but are more or less abstracted out with library's like Material UI. More on that in a minute, first let me present to you the quick and easy solution; in your index.js, utilize event listeners with vanilla html:
<div onClick={showHide}>
<Header />
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
You'll also want to use a boolean switch in cases like these:
function showHide(e) {
setIsClicked(!isClicked);
}
To listen to events, earlier versions used attachEvent:
element.attachEvent('onclick', function() { /* do stuff here*/ });
Modern browsers now support useEventListener:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
Here you can attach event listeners to as many elements as you want (memory permitting), including the users window element. That final parameter is a relic of pre-universal Javascript browser support, so now capture events and bubbling events are both supported. The default (false) is set to bubbling, where events 'bubble up' the DOM tree from the target node. More information on that here.
You can target your event listeners in Header.jsx to handle your events manually, and learn a little about JS event propagation. Since this is React, we want to utilize the useEffect, and useCallback hook to stop infinite rendering. Then well pass the callback function as props, so you can 'interact' with the Header component from index.js:
<div className="main">
<Header callback={showHide} />
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
then in Header. jsx:
import { useCallback, useEffect } from "react";
export default function Header(props) {
const handleClick = useCallback((event) => {
props.callback(event);
}, [props]);
useEffect(() => {
window.addEventListener("click", handleClick);
}, [handleClick]);
...
Note that the window element will target the whole component. To add events to individual html elements with id="header-component", use:
let element = document.getElementById("header-component");
Now for the third and I'd say best solution, utilizing React's props design pattern. Header.jsx:
export default function Header(props) {
return(
<Button onClick={props.callback}>Display Notifications</Button> //here we use the MUI button, but you can also use div or any other event listening element
...
);
}
and same thing again in index.js:
<div className="main">
<Header callback={showHide} />
{isClicked && <Notification />}
<h1> Sample body </h1>
<Footer />
</div>
In this case, you can use different callback functions to attach as many events as you want to one element, while relying on React to do the heavy lifting.
Im using Material UI's Stepper component and trying to get the fill of a stepper that is in its error state to be red.
For reference, here is the Stepper component in Material UI's docs.
So im trying to show an error state. Material UI's has an error prop that will show the error state, however, I dont want the icon provided.
I just want it to be like any other stepper, just with a red background.
Is there any way I can get rid of that icon, and just show a red fill?
Been search all over but seems like no one really asked about this.
Here is my code:
<Stepper alternativeLabel activeStep={this.determineFormStep()} connector={<StepConnector />} className={classes.stepper}>
{formLabels.map((label) => {
return (
<Step key={label}>
<StepLabel
error
StepIconProps={{
classes: {
root: classes.step,
completed: classes.completed,
active: classes.active,
error: classes.error,
disabled: classes.disabled
}
}}>
<div className={classes.stepLabelRoot}>
<Typography className={classes.label}>
{label.label}
</Typography>
<span className={classes.sublabel}>
{label.sublabel1}
</span>
<span className={classes.sublabel}>
{label.sublabel2}
</span>
<span className={classes.sublabel}>
{label.sublabel3}
</span>
</div>
</StepLabel>
</Step>);
})}
</Stepper>
The docs for StepLabel show that it can take an optional icon prop, which is a node that overrides the step icon.
As an example, if you wanted to hide the icon entirely you could pass a Fragment element as the icon:
<StepLabel
error
icon={<></>}
>
...
</StepLabel>
Or you can change the StepIconComponent or StepIconProps.
Here is an example turns off the error state on the icon only:
<StepLabel
error
StepIconProps={ {error: false} }
>
...
</StepLabel>
Hi I am trying to create a snackbar using material ui.
React version = 0.14
Material-ui version =0.15.0
I couldn't find anything suitable for this version of React so I decided to use material-ui. It works but only opens at the bottom of the screen. How can I change this? For example how can I get the top right.
My Code :
import Snackbar from 'material-ui/Snackbar';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'
this.state = {
open: false,
};
handleTouchTap = () => {
this.setState({
open: true,
});
};
handleRequestClose = () => {
this.setState({
open: false,
});
};
<div>
<button
onClick={this.handleTouchTap}>
SHOW NOTIFICATION
<button>
<Snackbar
open={this.state.open}
message="Event added to your calendar"
autoHideDuration={4000}
onRequestClose={this.handleRequestClose}
/>
</div>
Image is here : [Notification image1
Just add anchorOrigin to update position:
<Snackbar
...
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
/>
I've been trying to get my React component to work with the Collapse but I cannot seem to get my component to collapse correctly. Right now it will collapse temporarily when the div is clicked, but it will automatically reopen and not actually collapse any of the information needed. This component is taking multiple "modules" and turning them into their own cards. I've tried using a button instead of a div for the "onClick" and have tried with and without the reactstrap Card and CardBody components.
I'm thinking that the useState hook is somehow getting lost with my other props? Any help would be appreciated.
import React, { useState } from "react";
import { Container, Collapse, Card, CardBody } from "reactstrap";
import ReplayCard from "./ReplayCard";
import AttachmentCard from "./AttachmentCard";
const ModuleCard = (props) => {
const module = props.cardID;
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
<div className="moduleCard">
<div onClick={toggle}>
<h2>{module.m_title}</h2>
<h5>Replay Date: {module.m_date}</h5>
</div>
<Collapse isOpen={isOpen}>
<h5>
{module.m_description}
</h5>
<h3>{module.m_title} Video(s)</h3>
<Container className="ReplayCard" fluid={true}>
{module.m_replay &&
module.m_replay.map((value, index) => {
return (
<ReplayCard
key={index}
cardID={index}
video={value.video}
text={value.text}
/>
);
})}
</Container>
<h3>{module.m_title} Link(s)</h3>
<Container className="AttachmentCard" fluid={true}>
{module.m_attachment &&
module.m_attachment.map((value, index) => {
return (
<AttachmentCard
key={index}
cardID={index}
text={value.text}
link={value.link}
/>
);
})}
</Container>
</Collapse>
</div>
);
};
export default ModuleCard;
The useState does seem to be changing from true to false when a console.log is inserted to the togged but still isn't actually triggering any changes.
I have been working on my first Meteor application and am a bit stuck. I want to create my code following the latest guidelines (ES6 and React 15) but I am confused with all the recent changes in Javascript.
I want to add a Bootstrap Modal in my current comments list but can't seem to figure out how to add my content to the modal using the right up to date syntax.
Here is my current code:
In comment.js:
import React from 'react';
import { Row, Col, ListGroupItem, FormControl, Button } from 'react-bootstrap';
import { Bert } from 'meteor/themeteorchef:bert';
import { CommentsModal } from './comments-modal'
export const Comment = ({ comment }) => (
<ListGroupItem key={ comment._id }>
<Row>
<Col xs={ 8 } sm={ 10 }>
<FormControl
type="text"
defaultValue={ comment.title }
/>
</Col>
<Col xs={ 4 } sm={ 2 }>
<Button
bsStyle="danger"
className="btn-block">
Remove Comment
</Button>
</Col>
</Row>
<CommentsModal/>
</ListGroupItem>
);
In Comments-modal.js:
import React, { Component } from 'react';
import { Modal, Button, Tooltip } from 'react-bootstrap';
export class CommentsModal extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false,
};
this.close = this.close.bind(this);
this.open = this.open.bind(this);
}
close() {
this.setState({ showModal: false });
}
open() {
this.setState({ showModal: true });
}
render() {
return (
<div>
<Button
bsStyle="primary"
bsSize="large"
onClick={this.open}
>
</Button>
<Modal show={this.state.showModal} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title >Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Text in a modal</h4>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.close}>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
And last comments-list.js:
import React from 'react';
import { ListGroup, Alert } from 'react-bootstrap';
import { Comment } from './comment';
export const CommentsList = ({ comments }) => (
comments.length > 0 ? <ListGroup className="comments-list">
{comments.map((com) => (
<Comment key={ com._id } comment={ com } />
))}
</ListGroup> :
<Alert bsStyle="warning">No comments yet. Please add some!</Alert>
);
CommentsList.propTypes = {
comments: React.PropTypes.array,
};
I manage to get the Modal to show up and work but when I want to display data in it, I can't get it to work. What is the best way to combine both these into one?
Pass the data in props to the CommentsModal and render it as you would normally do.
I try to keep local state out of component when using redux if possible, so to answer your question on making it stateless, I would take the following steps:
Remove the button that opens the modal from the modal.js itself
Remove the actual modal from the modal.js, just put the modal content inside of there.
Change the open modal button to hook into an action creator that sets a prop to open the modal and passes it's content (also set one to close it)
So that looks something like this
<ListGroupItem key={ comment._id }>
<Row>
<Col xs={ 8 } sm={ 10 }>
<FormControl
type="text"
defaultValue={ comment.title }
/>
</Col>
<Col xs={ 4 } sm={ 2 }>
<Button
bsStyle="danger"
className="btn-block">
Remove Comment
</Button>
</Col>
</Row>
<!-- Here is where it changes, -->
<Button
bsStyle="primary"
bsSize="large"
onClick={this.props.openModal(comment)}
>
</Button>
<Modal show={this.props.commentModal} onHide={this.props.closeModal}>
<CommentsModal content={this.props.commentModal} />
</Modal>
Keep in mind, these naming conventions are just for examples sake : use whatever works best for you.
So what happens here is when you click that button you fire this.props.openModal (an action) which does something like this in the reducers -
case actions.OPEN_COMMENT_MODAL:
return state.set('commentModal', action.content);
the close buttons fire the onHide which is linked to the this.props.closeModal action which just does:
case actions.OPEN_COMMENT_MODAL:
return state.set('commentModal', undefined);
So what this allows you to do is have just 1 modal instance and you pass the current comment to it with that button click and open it. The show just checks the truthy value, so you set it back to undefined and it will hide itself.
Then I am passing the prop of content to the modal, so you can then use it inside the modal itself. Again, change the names to whatever works best for you.