Fix date picker inside modal window in react js - reactjs

I have this component:
const App = () => {
const [isModalVisible, setIsModalVisible] = useState(false);
const showModal = () => {
setIsModalVisible(true);
};
const handleOk = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
return (
<>
<Button type="primary" onClick={showModal}>
Open Modal
</Button>
<Modal
title="Basic Modal"
visible={isModalVisible}
onOk={handleOk}
onCancel={handleCancel}
>
<DatePicker
onChange={test}
open={true}
showNow={false}
dateRender={(current) => {
const style = {};
if (arr.includes(current)) {
style.border = "1px solid red";
style.borderRadius = "50%";
}
return (
<div className="ant-picker-cell-inner" style={style}>
{current.date()}
</div>
);
}}
/>
</Modal>
</>
);
};
When i open the modal i get the calendar outside it. How to put the calendar inside the modal window, not outside like now?
demo: https://codesandbox.io/s/basic-antd494-forked-6lkqg?file=/index.js:269-1303

This is, IMO, incredibly hackish, but was the only way I could get the date picker's popup to behave a bit more nicely nested in a modal.
Use the getPopupContainer prop to specify what the parent should be, it defaults to "body". We can create our own element to append the date popup to.
Use the popupStyle prop to override and set the position CSS rule. It is position: absolute by default, we wan't relative positioning.
Add an empty div after DatePicker for the popup to attach to.
HACK ALERT: DatePicker inserts a div between the one we created and the popup, so CSS rule cascading & inheritance gets fubar'd, it has position: absolute as well. We need to override this to also use relative positioning.
Code:
<>
<Button type="primary" onClick={showModal}>
Open Modal
</Button>
<Modal
title="Basic Modal"
visible={isModalVisible}
onOk={handleOk}
onCancel={handleCancel}
>
<DatePicker
getPopupContainer={() => document.getElementById("date-popup")}
popupStyle={{
position: "relative"
}}
onChange={test}
open={true}
showNow={false}
dateRender={(current) => {
const style = {};
if (arr.includes(current)) {
style.border = "1px solid red";
style.borderRadius = "50%";
}
return (
<div className="ant-picker-cell-inner" style={style}>
{current.date()}
</div>
);
}}
/>
<div id="date-popup" />
</Modal>
</>
CSS
#date-popup > div {
position: relative !important;
}
The main reason this is hackish is because the use of !important should generally be avoided and instead you should strive to increase a selector's specificity. I was unable to bump it enough to override the style. (I believe this is because that div is also using some inline style prop/attribute)

DatePicker itself is also a kind of Modal so it can't be embedded inside Modal. You can simply check this by inspecting both Modal and DatePicker components.
If you want the DatePicker to be opened, you can control the height of modal body. Something like this.
<Modal
title="Basic Modal"
visible={isModalVisible}
onOk={handleOk}
onCancel={handleCancel}
bodyStyle={{
height: 400
}}
>

Here is another suggestion:
<Modal
title="Basic Modal"
visible={isModalVisible}
onOk={handleOk}
onCancel={handleCancel}
centered
style={{ minHeight: `500px` }}
>

Related

Screen turns black while using antd modal

I have a list of rows in a table. This only happens when i have more rows in a table. I am using modal of antd.
This is my code:
import {Modal,Button} from "antd";
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const handleOk = (id) => {
store.dispatch(deleteProductList(id));
setIsDeleteModalVisible(false);
};
const handleCancel = () => {
setIsDeleteModalVisible(false);
};
<Button
icon={<DeleteFilled />}
style={{
backgroundColor: "#D15B47",
color: "#ffffff",
}}
onClick={showDeleteModal}
>
Delete
</Button>
<Modal
title="Confirm Delete"
visible={isDeleteModalVisible}
onOk={() => handleOk(record.id)}
onCancel={handleCancel}
>
<p>Are you sure you want to delete this vehicle?</p>
</Modal>
Not much to go on from the content in the question. But I assume that the backdrop has full opacity or a color that has an alpha of 1? Try to inspect the backdrop and see if the color is correct.

Material UI - closing modal leaves focus state on button that opened it

Let's say I have a button that opens a Dialog component. The button has custom theming/styling to specify various states, one of them being the :focus state:
const useStyles = makeStyles({
root: {
"&:focus": {
backgroundColor: "#3A7DA9"
}
}
});
export default function App() {
const [open, setOpen] = useState(false);
const classes = useStyles();
return (
<div className="App">
<Button
id="button-that-opens-modal"
className={classes.root}
onClick={() => setOpen(true)}
>
Open the modal
</Button>
<Dialog open={open}>
<h3>This is the modal</h3>
<Button onClick={() => setOpen(false)}>
Close
</Button>
</Dialog>
</div>
);
}
What I've noticed is that every time I have this pattern, (where a button opens a dialog modal), when the modal is closed, the #button-that-opens-modal is left with a :focus state, which looks bad in terms of styling. Here's a quick gif:
Codesandbox demonstrating the issue
Is this a known issue? I don't see why the :focus should be automatically applied to the button when the modal closes. How can I stop this?
I tried this:
I can add a ref to the button, and make sure to manually unfocus the button in various places. Adding it in the onExited method of the Dialog works, but flashes the focus state for a second:
export default function App() {
const [open, setOpen] = useState(false);
const buttonRef = useRef();
const classes = useStyles();
return (
<div className="App">
<Button
ref={buttonRef}
className={classes.root}
onClick={() => setOpen(true)}
>
Open the modal
</Button>
<Dialog
open={open}
TransitionProps={{
onExited: () => {
buttonRef.current?.blur(); // helps but creates a flash
}
}}
>
<h3>This is the modal</h3>
<Button onClick={() => {setOpen(false)}}>
Close
</Button>
</Dialog>
</div>
);
}
sandbox showing this very imperfect solution
And even if I found exactly the right event handler to blur the button such the styling looks correct, this is not something I want to do for every Dialog in an app that has many Button - Dialog pairs. Is there a Material-UI prop I can use to disable this 'auto-focus' back on the button, rather than having to create a ref and manually .blur it for every Dialog?
This is for accessibilty purpose. You can disable it by adding prop disableRestoreFocus on your Dialog :)

React - execute javascript after bootstrap modal is completely rendered and visible

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
);
};

How can I reduce the number of buttons?

I need to have only one button doing two things one by one. I have two buttons "Обучить" and "Генерировать" and they both do something onClick. Is it possible to do it all with one button?
<Button
variant="contained"
color="primary"
style={{
background:
"linear-gradient(45deg, #00ACD3 30%, #00BE68 90%)"
}}
onClick={this.parseInput}
>
Обучить
</Button>
<Button
variant="contained"
color="primary"
style={{
background:
"linear-gradient(45deg, #00ACD3 30%, #00BE68 90%)"
}}
onClick={() => {
this.props.updateData(this.state.filterArray);
}}
>
Генерировать
</Button>
Of course you can. You can use a variable to decide what action you want to perform on onClick function
onClick={() => {
if(this.props.type == 'update')
this.props.updateData(this.state.filterArray);
else
this.parseInput()
}}
You can use same property type (or anything you like) to render different labels
{{this.props.type=='update'? 'Генерировать': 'Обучить'}}
I assume that you want to make a component reusable? In that case you might want to design your component like so.
const NewButton = ({onClick, children}) => (
<Button
variant="contained"
color="primary"
style={{
background: "linear-gradient(45deg, #00ACD3 30%, #00BE68)
}}
onClick={onClick}>
children
</Button>);
Then your above snip becomes
<NewButton onClick={someFunction}>Something</NewButton>
<NewButton onClick={otherFunction}>Something</NewButton>
This is the basic idea behind any component-based front-end framework and I suggest you go to the react tutorials to learn more.
Otherwise, there are plenty of good articles about the topic out there
If I understand you correctly, you want to use the same component but make them do different things, on click for example.
Here is an example of how you can do it.
import React from 'react';
const Button = ({onClick}) => {
return (
<button
onClick={onClick}
>
Click on me
</button>
)
}
export default Button;
So when you want to pass down a method to the button, you can do it like this.
import Button from './button';
clickerTicker() {
alert("First method, clickerticker");
}
secondaryClick() {
alert("Second method yo");
}
render() {
return (
<div>
<ButtonComponent onClick={this.clickerTicker.bind(this)}/>
<br/>
<ButtonComponent onClick={this.secondaryClick.bind(this)}/>
</div>
);
}
Just pass the onClick to the component

Material-UI hover effect line offset bug (React)

I have been looking to implement Material-UI in my React ES6 project. As I have been trying things in a sandbox, I noticed a pretty significant styling quirk on the RaisedButton component. When hovering at times there is either a line at the top or bottom of the button where the hover effect doesn't completely overlap the button. The behavior changes when I override the margins. For instance when I have 8px margins the plain button has no line. When I add 6px margins the button has a line at the top. When I have a 4px margin it has a line at the bottom. This behavior changes also when there's different components next to each other in the HTML.
I am using Source Sans instead of Roboto, but switching back to Roboto has the same behavior. Likewise, keeping text-transform as uppercase instead of none has the same behavior.
I have tried wrapping the button in a div and applying margin or padding to that with the same result.
I am on the latest Chrome on Mac OSX 10.11.3.
Thanks in advance for your help.
I am wrapping the button in a custom class to allow consistent style overrides. Here's my wrappers:
Button Wrapper
...
let customTheme = ThemeManager.getMuiTheme(CustomTheme)
customTheme.button.textTransform = 'none'
const styles = {
button: {
margin: 8
}
}
#ThemeDecorator(customTheme)
class Button extends React.Component {
render() {
return (
<div className="c-button" style={{ display: 'inline-block' }}>
<RaisedButton
style={ styles.button }
label={ this.props.label }
onClick={ this.props.onClick }
primary={ this.props.primary }/>
</div>
)
}
}
...
Modal Wrapper
...
render() {
const actions = [
<Button
label="Cancel"
onClick={ this.props.handleClose } />,
<Button
label="Submit"
primary={ true }
onClick={ this.props.handleClose } />
]
return (
<Dialog
title="Dialog With Actions"
actions={ actions }
open={ this.props.open } >
Only actions can close this dialog.
</Dialog>
)
}
...
Plain RaisedButton by itself
...
<ul>
<li>
<h4>Plain Button</h4>
<Button label="Button" onClick={this.handleClick}/>
</li>
</ul>
...
Plain RaisedButton for Modal
...
<li>
<h4>Plain Modal</h4>
<Button label="Modal Dialog" onClick={ this.handleOpen } />
<Modal
open={ this.state.open }
handleClose={ this.handleClose }
handleOpen={ this.handleOpen } />
</li>
...
I'm hovering my mouse over the buttons in these screenshots. The only difference is the override of the margin in the first one to be 8px.

Resources