I'm using FullCalendar in React and need a modal to pop-up when a button is clicked.
The issue I'm experiencing is that certain elements of the calendar do not fade behind the overlay and are instead at full opacity and in front of the modal.
Specifically, in dayGridMonth view the elements are the internal grid that makes up the calendar, the date numbers in each cell, the events and the current day highlight (i.e. all internal calendar elements). However, the toolbar header, the day text and the external border are all hidden as desired.
When in listDay or listWeek views, only the active button in the toolbar is misbehaving.
I've tried implementing a plain JS modal and also using react-modal. The issue persists in both.
import { useState } from "react";
import TaskModal from './TaskModal';
function Calendar (props) {
const [modalOpen, setModalOpen] = useState(false);
function addEvent () {
setModalOpen(true);
}
return (
<div>
<FullCalendar
headerToolbar={{
start: "addEvent",
center: "foo",
end: "bar",
}}
customButtons={{
addEvent: {
text: "+",
click: addEvent,
}
}}
some other props...
>
<TaskModal modalOpen={modalOpen} setModalOpen={setModalOpen} />
</div>
);
}
import Modal from "react-modal";
export default function TaskModal (props) {
Modal.setAppElement("#root");
return (
<Modal
isOpen={props.taskModalVisible}
onRequestClose={false}
contentLabel="My dialog"
>
<div>My modal dialog.</div>
<button onClick={() => props.setTaskModalVisible(false)}>Close modal</button>
</Modal>
);
}
The issue was simply that the problematic elements had a very high CSS z-index (as high as 999). This was resolved by adding className and overlayClassName props to the Modal. Then giving these classes an even higher z-index so that everything else would be behind.
Related
I have an Antd popover, that by clicking a button inside its content, opens a modal.
I want to close the popover when the modal opens.
When I tried just passing the popover visibility state setter down to the modal as a prop, there was a problem. There was some kind of "collision" between the state of the modal and the passed down prop state of the popover:
Collision CodeSandbox example
I did find a workaround - creating the modal state variables in the parent component (the popover) and passing them down to the modal using props:
Working CodeSandbox example
First of all, you can notice that the modal isn't closing at it supposed to - there's no nice smooth animation minimizing it, it just suddenly disappears. For reference, you can look here to see how it should look like when closing.
So my question is - why did this collision happen? Is there a better way to solve it?
Thanks!
This collision happens because in show modal handler you set visibility of popover to false and hide it and ant-popover-hidden class add to it's div element so anything inside it would not display like Modal however you show modal but because of its parent it couldn't visible, so I think You must separate modal from the popover content and place it somewhere beside them like this:
const Test = () => {
const [isSharePopoverVisible, setIsSharePopoverVisible] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const handlePopoverVisibleChange = () => {
setIsSharePopoverVisible(!isSharePopoverVisible);
};
const handleOk = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
const showModal = () => {
setIsModalVisible(true);
setIsSharePopoverVisible(false);
};
return (
<>
<Popover
trigger="click"
title="Test"
visible={isSharePopoverVisible}
onVisibleChange={handlePopoverVisibleChange}
content={
<Button type="primary" onClick={showModal}>
Open Modal
</Button>
}
>
<Button>Test</Button>
</Popover>
<Modal
title="Basic Modal"
visible={isModalVisible}
onOk={handleOk}
onCancel={handleCancel}
>
<p>Some contents...</p>
</Modal>
</>
);
};
I'm trying to reset my form data when a modal closes. I think part of my problem is that the form data <Mint> is in another component.
Honestly, any time the modal is shown or hidden I would like to reset the data.
So if there is a way I can reset the form data inside the toggleModalOne() function that would be awesome, but I can't figure out how.
Any advice is greatly appreciated.
// BuyIt.jsx
import React, { useState } from "react";
import TextLoop from "react-text-loop";
import Modal from "react-modal";
import Mint from "../../components/slider/mint.js";
const BuyIt = () => {
const [isOpen, setIsOpen] = useState(false);
function toggleModalOne() {
setIsOpen(!isOpen);
}
return (
<div className="div-buyNowBtn">
<button id="buyNowBtn" className="white-fill-bg btn-outline btn-lg" onClick={toggleModalOne}>
Buy Now
</button>
</div>
<Modal
isOpen={isOpen}
onRequestClose={toggleModalOne}
contentLabel="My dialog"
className="custom-modal"
overlayClassName="custom-overlay"
closeTimeoutMS={500}
>
<Mint/>
</Modal>
);
}
You could wrap your Modal around a check to see if isOpen is true. This will make the whole modal re-render each time rather than just hiding and showing it but it still being in the DOM (which is what I am assuming is happening).
{ isOpen &&
<Modal
isOpen={isOpen}
onRequestClose={toggleModalOne}
contentLabel="My dialog"
className="custom-modal"
overlayClassName="custom-overlay"
closeTimeoutMS={500}
>
<Mint></Mint>
</Modal>
}
If you want a parent function to reset a child's form state, you probably will want to lift the state up to the parent and pass it down as a prop.
I'm using material UI for the collapse button, and the accordion.
I want people to be able to click the today button, and have that open the first accordion section (the one that says Saturday on it). The today button is inside a material UI toolbar. I'm also using react hooks for the project.
I'm just using the default accordion settings as shown on the material UI website. Each accordion has its own id (ex: panel1, panel2, etc.).
I'm using the sample layout from material ui controlled accordion
Example of Open Accordions:
Example of Closed Accordions:
Any help you could give me would be great! Thanks.
Using the expanded property of the Accordion along with state will give you what you are looking for.
Be aware that once you set the expanded property, the default behavior of the accordion will be overridden and you will need to control the behavior directly.
Here's a code sandbox example of how I would solve it.
edit - code example:
import {
Accordion,
AccordionDetails,
AccordionSummary,
Button
} from "#material-ui/core";
import React, { useState } from "react";
import "./styles.css";
const week = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
];
const d = new Date();
const currentDay = week[d.getDay()];
export default function App() {
const [openDay, setOpenDay] = useState("");
function handleAccordClick(day) {
if(openDay === day) setOpenDay("")
if(openDay !== day) setOpenDay(day)
}
function generateWeekAccordion(day) {
return (
<Accordion
expanded={day === openDay}
onClick={() => handleAccordClick(day)}
>
<AccordionSummary>{day}</AccordionSummary>
<AccordionDetails>{`${day} details...`}</AccordionDetails>
</Accordion>
);
}
return (
<div className="App">
<Button
style={{ marginBottom: "20px" }}
variant="outlined"
onClick={() => setOpenDay(currentDay)}
>
Open Today
</Button>
{week.map((day) => generateWeekAccordion(day))}
</div>
);
}
I'm working on a React front-end that uses Reactstrap, in which I'm creating my own reusable modal component. Whenever there is too much content for the modal, it becomes scrollable and to make that clear to the user, I created an indicator at the bottom of the modal. (example screenshot)
The indicator sticks at the bottom of the modal while scrolling and I make it disappear when the user reaches the end (check onscroll event and moreContent state in code below).
So far so good, but my problem is that I can't find a way to check if I initially have to show the indicator when rendering the modal. Right now the moreContent state is initially set to true, but that should depend on whether the modal is scrollable or not.
I tried:
to find an event like onScroll that fires when Modal is rendered so that I can check if event.target.scrollHeight == event.target.clientHeight
useEffect hook with a reference to the modal. This fires too soon because scrollHeight and clientHeight are still 0.
The code for my modal component:
import React, {useEffect, useState} from 'react';
import {Button, Modal, ModalBody, ModalHeader} from "reactstrap";
const MyModal = (props) => {
const [moreContent, setMoreContent] = useState(true);
const ref = React.useRef();
useEffect(() => {
console.log("scrollHeight", ref.current._element.scrollHeight);
console.log("scrollTop", ref.current._element.scrollTop);
console.log("clientHeight", ref.current._element.clientHeight);
});
const onScroll = (event) => {
if (moreContent) {
setMoreContent(event.target.scrollHeight - event.target.scrollTop !== event.target.clientHeight);
}
}
return (
<Modal isOpen={props.isOpen} toggle={props.closeHandler} centered={true} scrollable={true} className="my-modal" onScroll={onScroll} ref={ref}>
<ModalHeader tag="div" toggle={props.closeHandler}>
...
</ModalHeader>
<ModalBody>
{props.children}
{moreContent &&
<div className="modal-scroll-down">
<i className="fa fa-arrow-down mr-4"></i> MEER <i className="fa fa-arrow-down ml-4"></i>
</div>
}
</ModalBody>
</Modal>
)
};
MyModal.defaultProps = {
showCloseButton : true
}
export default MyModal;
Any tip, advice, workaround is welcome.
Thanks in advance!
The issue here is that you are attaching ref to a Modal which is not a DOM element and therefore will not have these properties scrollHeight, scrollTop, and clientHeight. Furthermore even if it was a DOM element, it is not the element with the scrollbar - it is actually ModalBody. But, to make matters worst, it looks like Reactstrap does not really expose a prop for you attach a forwarded ref to the ModalBody.
To solve this you can replace ModalBody with a div - this is where we can attach a ref to.
<Modal
isOpen={props.isOpen}
toggle={props.closeHandler}
centered={true}
scrollable={true}
className="my-modal"
onScroll={onScroll}
onOpened={onOpened}
>
<ModalHeader tag="div" toggle={props.closeHandler}>
modal header
</ModalHeader>
<div ref={ref} style={{ overflowY: "auto", padding: "16px" }}>
{props.children}
{moreContent && (
<div className="modal-scroll-down">
<i className="fa fa-arrow-down mr-4"></i> MEER{" "}
<i className="fa fa-arrow-down ml-4"></i>
</div>
)}
</div>
</Modal>
Pay attention to the onOpened prop I attached to <Modal>, this answers what you sought:
execute javascript after bootstrap modal is completely rendered and
visible
const onOpened = () => {
setMoreContent(
ref.current.scrollHeight - ref.current.scrollTop !==
ref.current.clientHeight
);
};
So I created this button component using the following code
const STYLES = ['btn--primary', 'btn--outline', 'btn--test'];
const SIZES = ['btn--medium', 'btn--large'];
const DISPLAY = ['btn--show', 'btn--hidden'];
export const Button = ({
children,
type,
onClick,
buttonStyle,
buttonSize,
buttonDisplay
}) => {
const checkButtonStyle = STYLES.includes(buttonStyle)
? buttonStyle
: STYLES[0];
const checkButtonSize = SIZES.includes(buttonSize) ? buttonSize : SIZES[0];
const checkButtonDisplay = DISPLAY.includes(buttonDisplay)
? buttonDisplay
: DISPLAY[0];
return (
<Link to='/sign-up'>
<button
className={`btn ${checkButtonStyle} ${checkButtonSize} ${checkButtonDisplay}`}
onClick={onClick}
type={type}
>
{children}
</button>
</Link>
);
So I have this button component inside of my navbar component and I also have it inside of my home page section component.
My issue is that whenever I shrink the page to mobile, I want to make the button component in the navbar to display: none and then on the home section I want it to show
What ends up happening is that since it's a component, any CSS style I add to it will go on any other page that is using the component, so basically my button disappears on the home page section when I need it to display
I tried to add an Id to the button component, but that didn't work
<Button id='nav-btn' buttonStyle='btn--outline'>
SIGN UP
</Button>
and I don't know how I'd add a custom class or id to the navbar button without it applying to all the other button components on my homepage
Hide it with an expression, e.g., { showButton && <Button /> }. If showButton is true, you'll see the button, if not, you won't.
If you want to do it via CSS, use a media-query to set display: none on whatever screen size it's supposed to disappear on.
Edit in response to the comment
#media (max-height: 960px) {
display: none;
}
That reads, "If the height is less than 960px, set this property."
If you want a "special" button that hides on a screen size, create a higher-order component that wraps your button.
const NinjaButton => () => {
// do stuff
return <Button cssOverrideProp={cssWithMediaQueryThatHides} />
}
In Button, you can conditionally apply that css,
className=`{/* your other css */ ${cssOverrideProp || ''}}`
Then you can use that button anywhere it's supposed to hide.