The Dialog component is not closing correctly - reactjs

I have a problem with the React.js Dialog component which contains a list of items. There is also an overlay in the Dialog. It closes automatically when the user clicks on it. But the scenario is something different. The dialog also includes a list of items that have some external links wrapped around the <a> element and some with the <div>.
But the method of closing the dialog is not working properly. The Dialog automatically closes when I click inside any list item.
Please see this:
My goal was to close the dialog whenever the user clicked on the overlay or whenever the user clicked on the list item wrapped with the <a> element. Otherwise, it should not be closed.
I still can't figure out What is the correct approach to tackle this issue?
CodeSandbox link
App.js
import React, { useState } from 'react';
import Dialog from './Dialog';
import './App.css';
const App = () => {
const [isOpen, setIsOpen] = useState(false);
const onClickHandler = (isFalse) => {
if (!isFalse) {
setIsOpen(false);
} else {
setIsOpen(!isOpen);
}
};
return (
<div>
<button type="button" onClick={onClickHandler}>
Open Dialog
</button>
<Dialog isOpen={isOpen} onClick={onClickHandler} />
</div>
);
};
export default App;
Dialog.js
import React from 'react';
const Dialog = (props) => {
const { isOpen, onClick } = props;
const onCloseHandle = () => {
onClick(false);
};
return (
<div
className={isOpen ? 'dialog-wrap open' : 'dialog-wrap'}
onClick={onCloseHandle}
>
<div className="dialog">
<ul className="dialog-list">
<li>
<a
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
React.js
</a>
</li>
<li>
<a
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Another Link
</a>
</li>
<li>
<div>Should Not Close onClick</div>
</li>
<li>
<div>Should Not Close onClick</div>
</li>
</ul>
</div>
</div>
);
};
export default Dialog;
App.css
*,:before,:after {
box-sizing: border-box;
}
.dialog-wrap.open {
display: flex;
}
.dialog-wrap {
display: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 16px;
background-color: rgba(0,0,0,0.66);
}
#media (min-width: 640px) {
.dialog-wrap {
padding: 48px 32px;
}
}
#media (min-width: 1024px) {
.dialog-wrap {
padding-left: 40px;
padding-right: 40px;
}
}
.dialog {
position: relative;
margin: auto;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 12px 16px rgba(0, 0, 0, 0.08);
border-radius: 4px;
width: 500px;
}
.dialog-list {
margin-bottom: 0;
padding-left: 0;
color: #333;
list-style: none;
}
.dialog-list > li {
border-bottom: 1px solid #eee;
padding: 24px;
}
.dialog-list > li > a {
color: #333;
display: block;
text-decoration: none;
}
.dialog-list > li > div {
cursor: pointer;
}

in this situation, I would create a .dialog-background element and give onCloseHandle to that.
I also hive my a elements the onCloseHandle as onClick prop as well
If you do not want to change your react tree you can decide that from the event like this:
<div
className={isOpen ? "dialog-wrap open" : "dialog-wrap"}
onClick={(e) => {
if (!e.target.closest('.dialog') || e.target.closest('a')) {
onClick(false)
}
}}
/>

I think your click event is propagating up the elements, and triggering onCloseHandle on your .dialog-wrap.
The simplest solution to this would be to use Event.stopPropagation() on the <div className="dialog">
E.g.
<div className="dialog" onClick={ e => e.stopPropagation() }>
(React technically uses "synthetic events", but they still include a stopPropagation option as long as it's being interacted with inside React)

Related

Why are my link tags delayed before activating in react

I set up a list of React Link tags in my website and when one gets clicked on, it should immediately automatically scroll down to the section the user clicks on. That works, however, for whatever reason there's about a 1-2 second delay before this action actually occurs after a click. I've been trying to figure out what is causing this but I can't figure out what the problem is.
Here is the NavBar component:
import React from "react";
import { Link } from 'react-scroll';
import { FaBars, FaTimes } from 'react-icons/fa';
import { useState, useEffect } from "react";
import './Navbar.css';
const Navbar = () => {
const [click, setClick] = useState(false);
const handleClick = () => setClick(!click);
const closeMenu = () => setClick(false);
const [fix, setFix] = useState(window.scrollY >= 950);
useEffect(() => {
const setFixed = () => {
setFix(window.scrollY >= 950);
};
window.addEventListener("scroll", setFixed);
return () => {
window.removeEventListener("scroll", setFixed);
};
}, []);
return (
<div className={fix ? 'header active' : 'header'}>
<nav className={fix ? 'navbar fixed' : 'navbar'}>
<div className='hamburger' onClick={handleClick}>
{click ? (<FaTimes size={30} style={{ color: '#ffffff' }} />)
: (<FaBars size={30} style={{ color: '#ffffff' }} />)}
</div>
<ul className={click ? "nav-menu active" : "nav-menu"}>
<li className='nav-item'>
<Link to="header" spy={true} smooth={true} offset={-100} onClick={closeMenu}>Home</Link>
</li>
<li className='nav-item'>
<Link to="aboutMe" spy={true} smooth={true} offset={-100} onClick={closeMenu}>About</Link>
</li>
<li className='nav-item'>
<Link to="portfolio" spy={true} smooth={true} offset={-100} onClick={closeMenu}>Projects</Link>
</li>
<li className='nav-item'>
<Link to="resume" spy={true} smooth={true} offset={-100} onClick={closeMenu}>Resume</Link>
</li>
</ul>
</nav>
</div>
)
}
export default Navbar;
Here is the css for the NavBar component:
.header {
position: fixed;
height: 90px;
width: 100%;
top: 0;
left: 0;
z-index: 1;
transition: .3s ease-in;
overflow: hidden;
background-color: transparent;
}
.header.active {
background-color: rgba(0,0,0,.9);
}
.header .navbar {
display: flex;
justify-content: center;
align-items: center;
margin: auto;
height: 100%;
padding: 0 1rem;
}
.header .nav-menu a {
color: #ffffff;
}
.header .nav-menu {
display: flex;
}
.header .nav-item {
padding: 1rem;
font-weight: 500;
}
.header .nav-item a:hover {
padding-bottom: 4px;
color: #d4af37;
cursor: pointer;
}
.nav-menu a.active {
color: #d4af37;
text-decoration: none;
}
.hamburger {
display: none;
}
.nav-item {
list-style: none;
}
#homeLink {
text-decoration: none;
}
#media screen and (max-width:940px) {
.header {
max-width: 100%;
background-color: rgba(0,0,0,.9);
}
.header .navbar {
max-width: 100%;
justify-content: space-between;
}
.hamburger {
display: block;
}
.nav-menu {
position: fixed;
left: -100%;
top: 90px;
flex-direction: column;
background-color: rgba(0,0,0,.9);
width: 100%;
height: 90vh;
z-index: 999;
text-align: center;
transition: .3s;
}
.nav-menu.active {
left: 0;
}
.nav-item {
margin: 1.5rem 0;
}
.header .navbar img {
width: 150px;
}
}
Played around with it a bit more. I found that the smooth={true} prop in the Link tags was what slowed it down. Not entirely sure why that was the case, but removing it made my navbar work as intended.

astro react component not getting styled with styled component

I'm importing this navbar component into an astro file, the component shows up on the page but the but the styling from styled-component is not working and it is throwing an error in my code editor that says:
Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
import React from 'react';
import { useState } from 'react';
import styled from 'styled-components'
import { AiOutlineClose, AiOutlineMenu } from 'react-icons/Ai'
import { Link } from 'react-scroll';
function Navigation() {
const [navb, setNavb] = useState(true);
const handleClick = () => setNavb(!navb) ;
const [click, setClick] = useState(true)
const handleMenu= () => setNavb(!navb)
const iconStyle = {width: "30px" , height:"30px", color: "#ffffff", }
const linkStyleDesktop = { color: "#000000", padding: "0 10px ", cursor: "pointer"}
const linkStyleMobile = { color: "#ffffff", padding: "30px 0", fontSize: "1.5rem", cursor: "pointer"}
return (
<Header className = "style">
<Container>
LOGO
<Nav className="desktop-nav">
<div className="desktop-navItems" >
<Link style = {linkStyleDesktop} to="/" smooth={true} duration={500}>
Home
</Link>
<Link style = {linkStyleDesktop} to="about" smooth={true} duration={500}>
About
</Link>
<Link style = {linkStyleDesktop} to="portfolio" smooth={true} duration={500}>
Portfolio
</Link>
<Link style = {linkStyleDesktop} to="footer" smooth={true} duration={500}>
Contact
</Link>
</div>
</Nav>
<button className = "btn" onClick = {handleClick}> {!navb ? <AiOutlineClose style={iconStyle}/>: <AiOutlineMenu style={iconStyle} /> }
</button>
</Container>
<Nav className="mobile-nav">
<div className={!navb || !click ? 'display-mobile-nav' : 'display-none'} >
<Link onClick={handleMenu} style = {linkStyleMobile} to="app" smooth={true} duration={500}>
Home
</Link>
<Link onClick={handleMenu} style = {linkStyleMobile}to="about" smooth={true} duration={500}>
About
</Link>
<Link onClick={handleMenu} style = {linkStyleMobile} to="portfolio" smooth={true} duration={500}>
Portfolio
</Link>
<Link onClick={handleMenu} style = {linkStyleMobile} to="footer" smooth={true} duration={500}>
Contact
</Link>
</div>
</Nav>
</Header>
)
}
export default Navigation;
const Header = styled.header`
background-color: #262b33;
padding: 1rem 0;
width: 100vw;
position:fixed;
z-index: 999;
.display-mobile-nav {
display: block;
}
.display-none{
display: none;
}
.desktop-nav {
display: none;
}
.mobile-nav {
.display-mobile-nav {
display:flex;
flex-direction: column;
align-items: center;
box-shadow: 10px 10px 5px 0px rgba(189,189,189,0.75);
-webkit-box-shadow: 10px 10px 5px 0px rgba(189,189,189,0.75);
-moz-box-shadow: 10px 10px 5px 0px rgba(189,189,189,0.75);
transition: opacity 5s ease-in;
}
}
`;
//container for desktop nav
const Container = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
padding:0 8rem;
font-family:Arial, Helvetica, sans-serif;
#media (max-width: 1200px){
padding: 1rem 1rem;
}
a {
color: #ffffff;
}
button {
background-color: transparent;
border: none;
}
#media (min-width: 800px){
.desktop-nav {
display: block;
}
.btn {
display: none
}
.mobile-nav {
display: none;
.display-mobile-nav {
display: none;
}
}
}
`
const Nav = styled.div`
.desktop-navItems {
display: flex;
li {
padding: 0 10px;
}
}
#media (min-width: 800px){
.mobile-nav {
display: block;
}
`;
Warning: Invalid hook call....
To resolve this error modify the first line of your code to;
export default function Navigation() {
and remove the default function export referenced lower in the code.
I haven't yet figured out why but astro.js seems to take issue
with naming convention for default exports on functional components.
I can't resolve the styling issue unfortunately.

Closing modal window by clicking on backdrop (ReactJS)

Could you please help me with one issue? I'd like to make a closing modal window by clicking on backdrop (using ReactJS). But in result window is closing even i click on modal window (at any position).
Here is my code:
import React from "react";
import { Fragment } from "react";
import ReactDOM from "react-dom";
import "./_Modal.scss";
const Backdrop = (props) => {
return (
<div className="backdrop" onClick={props.onClose}>
{props.children}
</div>
);
};
const ModalOverlay = (props) => {
return <div className="modal">{props.children}</div>;
};
const portalItem = document.getElementById("overlays");
const Modal = (props) => {
return (
<Fragment>
{ReactDOM.createPortal(
<Backdrop onClose={props.onClose}>
<ModalOverlay>{props.children}</ModalOverlay>
</Backdrop>,
portalItem
)}
</Fragment>
);
};
export default Modal;
And here is CSS:
.backdrop {
position: fixed;
top:0;
left: 0;
width: 100%;
height: 100vh;
z-index: 20;
background-color: rgba(0,0,0, 0.7);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: auto;
padding-bottom: 15vh;
}
.modal {
position: relative;
max-width: 70%;
top: 5vh;
background-color: $darkgrey;
padding: 1rem;
border-radius: 1.5rem;
box-shadow: 0 1rem 1rem rgba(0,0,0, 0.25);
z-index: 30;
}
}
I'm just start to learning frontend, therefore do not judge strictly )
Just for understanding other people: after adding event.stopPropagation() in ModalOverlay everything works!
const ModalOverlay = (props) => {
return (
<div
className="modal"
onClick={(event) => {
event.stopPropagation();
}}
>
{props.children}
</div>
);
};

Next js font awesome and google fonts

Hoping to get some help on an issue I have been having in Next js. I am using Fontawesome-react package for icon imports which is working fine but when I attempt to load in a google font style sheet into the head of my main component, it changes the fonts but makes the font awesome icons disappear. I have tried a couple of different solutions such as loading the font locally and utilizing _document and _app components but none fix the issue I am having. Everything ends up changing the font but still gets rid of all the font awesome icons from every part of my app. I will copy in the Nav component I have and the Layout component that holds everything.
Thank you for any help!
import React from 'react'
import Nav from './nav/Nav'
import Head from 'next/head'
import Footer from './Footer'
type Props = {
title?: string
}
const Layout: React.FC<Props> = ({ children, title = 'Macros' }) => {
return (
<div>
<Head>
<title>{title}</title>
<link
href="https://fonts.googleapis.com/css?family=Darker+Grotesque&display=swap"
rel="stylesheet"
/>
</Head>
<Nav />
<div id="pageContent">{children}</div>
<Footer />
<style jsx global>{`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
h1,
h2,
h3,
h4,
h5 {
font-weight: lighter;
font-family: 'Darker Grotesque', sans-serif;
}
#pageContent {
padding: 8rem 1rem;
}
`}</style>
</div>
)
}
export default Layout
import React, { useState } from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faBars, faSearch, faPlus } from '#fortawesome/free-solid-svg-icons'
import Link from 'next/link'
const NavBar: React.FC = () => {
const [navBarStatus, isOpen] = useState<Boolean>(false)
const toggleMenu = () => {
isOpen(navBarStatus ? false : true)
}
return (
<nav id="navContainer">
<div id="mobileNav">
<ul id="navBarTop">
<li>
<button type="button" className="navIcon" onClick={toggleMenu}>
<FontAwesomeIcon icon={faBars} />
</button>
</li>
<li>
<Link href="/">
<a>
<h3>Macro</h3>
</a>
</Link>
</li>
<li>
<button type="button" className="navIcon">
<FontAwesomeIcon icon={faSearch} />
</button>
</li>
</ul>
<ul id="navbarBottom">
<li>Top</li>
<li>New</li>
<li>Protein</li>
<li>Carbs</li>
<li>Fats</li>
</ul>
</div>
<div id="appDrawer">
<ul id="topIcons">
<li>
<button type="button" className="navIcon" onClick={toggleMenu}>
<FontAwesomeIcon icon={faBars} />
</button>
</li>
<li>
<button type="button" className="navIcon">
<FontAwesomeIcon icon={faPlus} />
</button>
</li>
</ul>
<nav>
<ul id="menuList">
<Link href="/">
<a>
<p>Home</p>
</a>
</Link>
<Link href="/">
<a>Favorites</a>
</Link>
<Link href="/">
<a>Notifications</a>
</Link>
<Link href="/">
<a>Login/Sign up</a>
</Link>
</ul>
</nav>
</div>
<style jsx>{`
#navContainer {
background-color: #504761;
color: white;
position: fixed;
width: 100%;
}
#navContainer a {
text-decoration: none;
color: white;
}
#mobileNav {
display: ${navBarStatus ? 'none' : ''};
}
#navBarTop {
color: white;
display: flex;
justify-content: space-between;
list-style: none;
padding: 1rem 1rem 0.5rem 1rem;
font-size: 1.5rem;
}
.navIcon {
color: white;
border: none;
background-color: #504761;
font-size: 1.5rem;
}
#navbarBottom {
display: flex;
list-style: none;
justify-content: space-evenly;
padding: 1rem 0;
}
#appDrawer {
display: ${navBarStatus ? 'block' : 'none'};
background-color: #504761;
height: 100vh;
}
#topIcons {
display: flex;
justify-content: space-between;
list-style: none;
padding: 1rem;
font-size: 1.5rem;
}
#menuList {
padding: 2rem 0 2rem 1rem;
display: flex;
flex-direction: column;
}
#menuList a {
font-size: 2rem;
margin: 1rem 0;
}
`}</style>
</nav>
)
}
export default NavBar

React-modal hides behind elements

I am trying to make use of react-modal for the first time. When I click on the sign-in button, the react-modal component is invoke but seems to be hiding behind the cover page which is a video landing page.
The React devtool displays the appropriate states before the sign-in button is clicked
before the sign-in button is clicked
When the sign-in button is now clicked, the react devtool now displays that the ModalPortal component is rendered showing the appropriate states
when the sign-in button is clicked
SignInModal.scss
.ReactModalPortal>div {
opacity: 0;
}
.ReactModalPortal .ReactModal__Overlay {
align-items: center;
display: flex;
justify-content: center;
transition: opacity 200ms ease-in-out;
}
.ReactModalPortal .ReactModal__Overlay--after-open {
opacity: 1;
}
.ReactModalPortal .ReactModal__Overlay--before-close {
opacity: 0;
}
.modal {
position: relative;
background: #464b5e;
color: white;
max-width: 90rem;
outline: none;
padding: 3.2rem;
text-align: center;
}
.modal__title {
margin: 0 0 1.6rem 0;
}
.modal__body {
font-size: 2rem;
font-weight: 300;
margin: 0 0 3.2rem 0;
word-break: break-all;
}
CoverPage.js Component
import Header from './Header';
import HeaderVideo from './HeaderVideo';
import SignInModal from './SignInModal';
import React, { Component } from 'react';
class CoverPage extends Component {
state = {
modalIsOpen: false
};
onOpenModal = () => {
this.setState(() => ({
modalIsOpen: true
}));
};
onCloseModal = () => {
this.setState(() => ({
modalIsOpen: false
}));
};
render() {
return (
<div>
<Header />
<HeaderVideo onOpenModal={this.onOpenModal} />
<SignInModal
modalIsOpen={this.state.modalIsOpen}
onOpenModal={this.onOpenModal}
onCloseModal={this.onCloseModal}
/>
</div>
);
}
}
export default CoverPage;
HeaderVideo.js Component
import React from 'react';
import Signup from './Signup';
import CoverInfo from './CoverInfo';
const HeaderVideo = props => {
return (
<div className="video-container">
<video preload="true" autoPlay loop volume="0" postoer="/images/1.jpg">
<source src="images/vine.mp4" type="video/mp4" />
<source src="images/vine1.webm" type="video/webm" />
</video>
<div className="video-content">
<div className="container content">
<div className="row">
<div className="col-md-9">
<CoverInfo onOpenModal={props.onOpenModal} />
</div>
<div className="col-md-3">
<Signup />
</div>
</div>
</div>
</div>
</div>
);
};
export default HeaderVideo;
CoverInfo.js Component
import React from 'react';
const CoverInfo = props => {
return (
<div className="info">
<div>
<h1>Welcome to EventCity!</h1>
</div>
<div>
<p>
At EventCity! we pride ourselves on the unrivalled personal {`event`} services,we provide
to our clientele. We guide you from the stressful decision making {`process`},ensuring you
are comfortable,whether it is a wedding, corporate {`function `}or even a kiddies party,we
create a buzz around you, taking you to the next level.
</p>
</div>
<div>
<h3>Innovation, {`Performance`} and Delivery</h3>
</div>
<button type="button" className="btn btn-success btn-lg" onClick={props.onOpenModal}>
Sign In here
</button>
</div>
);
};
export default CoverInfo;
video-cover.scss
video {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
z-index: 1;
}
.video-content {
z-index: 2;
position: absolute;
background: rgba(0, 0, 0, 0.6);
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.content {
padding-top: 120px;
}
You need to set the z-index property on the Modal's overlay, which normally has a z-index of 0. The CSS class is .ReactModal__Overlay
Here is the pure-React way of doing it:
const customStyles = {
content : {
...
},
overlay: {zIndex: 1000}
};
<Modal style={customStyles}>
...
</Modal>
.modal {
position: fixed;
z-index:9999;
top :0;
left:0;
right:0;
bottom:0;
background: #464b5e;
color: white;
outline: none;
padding: 3.2rem;
text-align: center;
}
Example of react-modal inline styles Set the styles in the react-modal inline styles. The z-index to 100 but make just like below
style={{
overlay: {
zIndex: 100,
backgroundColor: 'rgba(70, 70, 70, 0.5)',
},

Resources