I am trying to display a modal component on top on another modal component. When a button inside class ChannelDetail (first modal) is clicked, another modal is supposed to open on top (class Modal). However, it is opening behind the first modal and the states from the first modal is not properly being passed to the second modal. How can I get the second modal to open properly with the proper props?
import React, { Component } from 'react'
import './style.css'
import { ExcelRenderer} from 'react-excel-renderer';
import Modal from './Modal'
export class ChannelDetail extends Component {
state = { rows: [], cols: [], outage: '', d: 'ande' };
showOutageFiles = (e) => {
e.preventDefault();
this.setState({ outage: e.target.value })
document.querySelector('.bg-modal-outage').style.display = 'block';
}
fileHandler = (event) => {
let fileObj = event.target.files[0];
//just pass the fileObj as parameter
ExcelRenderer(fileObj, (err, resp) => {
if(err){
console.log(err);
} else{
this.setState({
cols: resp.cols,
rows: resp.rows
});
}
});
}
render() {
const { channelSelectedData } = this.props
if (this.props.channelSelectedData.length >= 1 && this.props.datatype != 'slar'){
return(
<div>
<div> <Modal data={this.state.outage} type={this.state.d}/> </div>
<div className="channel-detail-box">
<p>Channel Selected: {this.props.channelSelectedData[0].channel}</p>
<p>Inspected: {this.props.channelSelectedData.length} time(s)</p>
<p>Last Inspection Date: {this.props.channelSelectedData[0].scanned}</p>
<p>Outages: {channelSelectedData.map(({ outage }) => <button value={outage} onClick={this.showOutageFiles}>{outage + ', '}</button>)}</p>
</div>
</div>
)
} else {
return (
<div>
<p>No data found</p>
</div>
)
}
}
}
export default ChannelDetail
import React, { Component } from 'react'
import './style.css'
export class Modal extends React.Component {
// closes modal for outage files
hideOutageFile = () => {
document.querySelector('.bg-modal-outage').style.display = 'none';
}
render () {
// ANDE reports linked on the outage modal popup
const reportsANDE = {
'P1-1987': 'http://192.168.191.128:8080/P-IR-1-03650-1_R000(P1-1987).pdf',
'P1-1992': 'http://192.168.191.128:8080/PA-IR-92-1-03640-31_R001(P1-1992).pdf',
'P0211': 'http://192.168.191.128:8080/NA44-IR-03641-00001_R001(P0211).pdf',
'P1011': 'http://192.168.191.128:8080/NA44-IR-31100-00002.pdf',
}
// ANDE excel files linked on the outage modal popup
const excelANDE = {
'P1-1987': 'http://192.168.191.128:8080/Historical_Directory_PNGS_1-2018.xlsx',
'P1-1992': 'http://192.168.191.128:8080/Historical_Directory_PNGS_1-2018.xlsx',
'P0211': 'http://192.168.191.128:8080/Historical_Directory_PNGS_1-2018.xlsx',
'P1011': 'http://192.168.191.128:8080/Pickering-P1011-May-Verified_Results5.xls',
}
const prop = 'text-align';
const textStyle = { [prop]: 'center' };
console.log(this.props.data)
// Modal popup for downloading ANDE outage files
if (this.props.type === 'ande'){
return (
<div className="bg-modal-outage">
<div className="modal-outage-content">
<span className="close-Btn" onClick={this.hideOutageFile}>×</span>
<h2 style={textStyle}>{this.props.data}</h2>
<p>
</i> <br/>
</i>
</p>
</div>
</div>
)
}
}
}
export default Modal
Suggestion
try using z-index
link enter link description here
Your .bg-modal-outage component could receive a property that is passed through
<Modal data={this.state.outage} type={this.state.d}/> </div>.
Something like:
<Modal data={this.state.outage} type={this.state.d} isActive={yourClickEvent}/> </div>
And in the Modal component use something like
<div className="bg-modal-outage" style={{ display: isActive ? "block" : "none" }}>
Related
I am new to React and let's suppose we have the following piece of code.
const showDetails = () => {
console.log('Show details')
}
const template = (
<div>
<h1>Vibility Toggle</h1>
<button onClick={showDetails}>Show details</button>
</div>
)
var app = document.getElementById('app')
ReactDOM.render(template, app)
What I want is to change the button's title from Show Details to Hide Details when it is clicked. So I' ve thinking to get the click event inside the function and change the title by using a ternary operator. How to do that?
Thanks
Theo.
you can use the state to handle toggle event in react. I have added a snippet of toggle handler.
import React, { Component } from 'react';
class App extends Component {
state = {
isVisible : true
}
toggleDetails = () => {
this.setState({
isVisible : !this.state.isVisible
});
}
render() {
return (<div>
<h1>Vibility Toggle ({this.state.isVisible ? "is Visible" : "is Not Visisble"})</h1>
<button onClick={this.toggleDetails}>{!this.state.isVisible ? "Show details" : "Hide details"}</button>
</div>)
}
}
export default App;
You can also keep it simple by doing something like:
let show = true;
const showDetails = event => {
show = !show;
event.target.innerHTML = show ? "Show Details" : "Hide Details";
};
const template = (
<div>
<h1>Vibility Toggle</h1>
<button onClick={showDetails}>Show Details</button>
</div>
);
var app = document.getElementById("app");
ReactDOM.render(template, app);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
you need to use state in React.please read this documentation for state in React
React State.
here is the sample code to change title using ternary operator in React
import React, { Component } from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showDetails: true
}
}
showDetails = () => {
console.log('Show details')
this.setState({ showDetails: !this.state.showDetails })
}
render() {
return (
<div>
<h1>Vibility Toggle</h1>
<button onClick={() => this.showDetails()}>{this.state.showDetails ? 'Show Details' : 'Hide Details'}</button>
</div>
)
}
}
export default App
I am trying to create something similar to React Bootstrap's dropdown component. My starting skeleton is something like the following:
import React from 'react';
const DropDown = props => {
return <div className="dropdown-container">{props.children}</div>;
};
const DropDownToggle = props => {
return <div className="dropdown-toggle">{props.children}</div>;
};
const DropDownContent = props => {
return <div className="dropdown-content">{props.children}</div>;
};
export { DropDown, DropDownToggle, DropDownContent };
These components would be used like this:
<DropDown>
<DropDownToggle>
{/*
The content inside here should be customizable so the user of
these components can specify whatever they want for the toggle
*/}
<button type="button">
my button
</button>
</DropDownToggle>
<DropDownContent>
{/*
The content inside here should be customizable so the user of
these components can specify whatever they want for the content of
the dropdown
*/}
<ContentComponent/>
</DropDownContent>
</DropDown>
Is there a way I can communicate between the two children components (DropDownContent and DropDownToggle)? I have access to the parent component and it just receives and displays the children so far, but I would like to somehow communicate between the children so that the user can click on the toggle to open/close the content. I don't want to use redux.
Thank you in advance!
EDIT
I ended up going with the method that #Train suggested in his/her comment below. I was originally hoping for the ability to nest components manually, but what was most important to me was having the state be self-contained in the parent component. Being able to define the toggle button's HTML as well as the content's HTML was also a requirement. My final implementation allows for both of these things and looks something like this:
import React from 'react';
import PropTypes from 'prop-types';
export class Dropdown extends React.Component {
state = {
isOpen: false,
};
onDropDownToggleClick = () => {
this.setState({ isOpen: !this.state.isOpen });
};
render() {
let contentClasses = 'dropdown-content';
if (this.state.isOpen) {
contentClasses += ' show';
}
return (
<div className="dropdown-container">
<div className="dropdown-toggle" onClick={this.onDropDownToggleClick}>
{this.props.toggle}
</div>
<div className={contentClasses}>{this.props.content}</div>
</div>
);
}
}
Dropdown.propTypes = {
toggle: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
content: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
.isRequired,
};
export default Dropdown;
to use it:
const dropDownToggle = (
<button type="button">
Dropdown
</button>
);
const dropDownContent = 'content';
<DropDown
toggle={dropDownToggle}
content={dropDownContent}
/>
For something like toggling content you can use composition instead of inheritance to pass data around.
From the example of Facebook
This is done with props.children property.
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
In the render() I am rendering the Dialog component and passing in the props.
the props are .children and the custom props title, message
This lets us pass child elements directly into the output we can even add components from other classes as I did with the SignUpDialog.
Did you have something like this in mind?
const actionTypes = {
TOGGLE: "TOGGLE"
};
const notRedux = {
actionHandlers: Object.keys(actionTypes).reduce(
(acc, val) => ({ [val]: [], ...acc }),
{}
),
dispatchAction(actionType, data) {
this.actionHandlers[actionType].forEach(handler => handler(data));
},
onAction(actionType, actionHandler) {
this.actionHandlers[actionType].push(actionHandler);
}
};
const DropDown = ({ children }) => {
return <div className="dropdown-container">{children}</div>;
};
const DropDownToggle = () => {
const onClick = () =>
notRedux.dispatchAction(actionTypes.TOGGLE, "oh hi Mark");
return (
<div className="dropdown-toggle">
<button type="button" onClick={onClick}>
my button
</button>
</div>
);
};
const DropDownContent = props => {
notRedux.onAction(actionTypes.TOGGLE, data =>
alert(`DropDownToggle said ${data} //DropDownContent`)
);
return <div className="dropdown-content">{props.children}</div>;
};
const App = () => (
<DropDown>
<DropDownToggle></DropDownToggle>
<DropDownContent>
<span>Content goes here</span>
</DropDownContent>
</DropDown>
);
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></app>
I'm trying to hide a component when clicking a Yes or Not. I'm getting the correct output when clicking but the component that acts like a Popup keeps in the screen and I can not remove it.
This is my component
export default class MyPopUp extends React.Component {
constructor(props) {
super(props)
}
render() {
let { username } = this.props
return (
<div className="modal-wrapper">
<div className="modal">
Some Text
<br />
<MyButton
name={username}
surname="pewlas"
arguments={["yes"]}
>
[ Yes ]
</PostCommandButton>{" "}
<MyButton
name={username}
surname="pewlas"
arguments={["no"]}
>
[ No ]
</MyButton>{" "}
</div>
</div>
)
}
}
And this is MyButton
import { connect } from "react-redux"
import button from "../../../common/components/Button"
import { myButton } from "../actions/myButton"
const MyButton = connect(
undefined,
(dispatch, { name, surname, arguments: args = [] }) => ({
onClick: () => dispatch(button(name, username, args)),
}),
)(Button)
export default MyButton
And this is my button
export const MY_BUTTON = "MY_BUTTON"
export const myButton = (name, surname, args) => ({
type: MY_BUTTON,
name,
surname,
arguments: args,
})
How can I hide MyPopUp when clicking Yes or No? I do not find the way to link my PopUp component with the onClick from MyButton, how can I add a state so I can show it or not?
I'm going to try to help you maybe refactorizing some code.
First of all, I have seen that you are using redux with your Button, to do what you want to do you have some options:
const SURNAME = "pewlas"
export class MyPopUpComponent extends React.Component {
state = { hidden: false }
handleClick = value => {
this.props.myButton(this.props.username, SURNAME, value) //the action creator
this.setState({ hidden: true }) //**IMPORTANT**
}
render() {
const { username } = this.props
if (this.state.hidden) {
return null
}
return (
<div className="modal-wrapper">
<div className="modal">
Some Text
<br />
<button
name={username}
surname={SURNAME}
onClick={() => this.handleClick("yes")}
>
[ Yes ]
</button>{" "}
<button
name={username}
surname={SURNAME}
onClick={() => this.handleClick("no")}
>
[ No ]
</button>{" "}
</div>
</div>
)
}
}
const mapDispatchToProps = {
myButton
}
export const MyPopUp = connect(null, mapDispatchToProps)(MyPopUpComponent)
You can do something like that, first of all, you show the popup initializing state hidden to false. As you can see in the render you are going to show the text with buttons and as soon as a user press yes or now you can launch an action or do whatever you want and after that, you set hidden to true. The state has changed so the render function is called again returning null, hiding the component.
Hope it helps you
I have a header component which manages the state for my navigation component.
The navigation successfully toggles if the user clicks on the hamburger icon however, if the user clicks or taps anywhere outside of the navigation I need the navigation to close.
How can I achieve this?
Here is my code:
export default class Header extends React.Component {
constructor() {
super();
this.state = {
mobileOpenNav: false
};
bindAll([
'openMobileNav',
'openContactModal'
],this);
}
openMobileNav() {
this.props.contactModalToggle(false);
this.setState({
mobileOpenNav: !this.state.mobileOpenNav
})
}
openContactModal() {
this.props.contactModalToggle();
this.setState({
mobileOpenNav: !this.state.mobileOpenNav
});
}
render() {
const {nav, contactModalToggle, location, logos} = this.props;
const {mobileOpenNav} = this.state;
return (
<div className="header-wrap">
<div className="header">
<Logo location={location} logoUrls={logos} />
<Navigation
location={location}
nav={nav}
contactModalToggle={this.openContactModal}
mobileOpen={mobileOpenNav}
mobileToggle={this.openMobileNav}
/>
<div className="hamburger" onClick={this.openMobileNav}><img src={HamburgerIcon} /></div>
</div>
</div>
)
}
}
The following solution should work for you.
componentDidMount() {
document.addEventListener('click', this.handleClickOutside.bind(this), true);
}
componentWillUnmount() {
document.removeEventListner('click', this.handleClickOutside.bind(this), true);
}
handleClickOutside(e) {
const domNode = ReactDOM.findDOMNode(this);
if(!domNode || !domNode.contains(event.target)) {
this.setState({
mobileOpenNav: false
});
}
}
use react-onclickoutside module https://github.com/Pomax/react-onclickoutside
import onClickOutside from "react-onclickoutside"
import Navigation from "pathToNvaigation"
const ContentWrapper = onClickOutside(Navigation)
and use
<ContentWrapper
location={location}
nav={nav}
contactModalToggle={this.openContactModal}
mobileOpen={mobileOpenNav}
mobileToggle={this.openMobileNav}
/>
I'm using React and firebase to create a simplified slack and using MDl for styles. I'm trying to integrate a button that opens up a dialog box to get some user input, in this case the name of a new chat room, and then when submitted it will send the data to firebase and store it in the rooms array and display the new room name in the list. I have already set up the form to get user input and then I tried to refactor it to work in a dialog box and I seem stuck on how to get the dialog box to work. Here is my whole component:
import React, { Component } from 'react';
import './RoomList.css';
import dialogPolyfill from 'dialog-polyfill';
class RoomList extends Component {
constructor(props) {
super(props);
this.state = { rooms: [] };
this.roomsRef = this.props.firebase.database().ref('rooms');
this.handleChange = this.handleChange.bind(this);
this.createRoom = this.createRoom.bind(this);
}
handleChange(e){
this.setState({ name: e.target.value });
}
createRoom(e) {
e.preventDefault();
this.roomsRef.push({ name: this.state.name });
this.setState({ name: "" });
}
componentDidMount() {
this.roomsRef.on('child_added', snapshot => {
const room = snapshot.val();
room.key = snapshot.key;
this.setState({ rooms: this.state.rooms.concat( room ) });
})
}
dialogBox(e){
const dialogButton = document.getElementsByClassName('dialog-
button');
const dialog = document.getElementById('dialog');
if (! dialog.showModal) {
dialogPolyfill.registerDialog(dialog);
}
dialogButton.onClick( (e) => {
dialog.showModal();
});
dialog.document.getElementsByClassName('submit-close').onCLick(
(e) => {
dialog.close();
});
}
render() {
const roomlist = this.state.rooms.map( (room) =>
<span className="mdl-navigation__link" key={room.key}>
{room.name}</span>
);
const newListForm = (
<div id="form">
<button className="mdl-button mdl-js-button mdl-button--
raised mdl-js-ripple-effect dialog-button">Add room</button>
<dialog id="dialog" className="mdl-dialog">
<h3 className="mdl-dialog__title">Create Room name</h3>
<div className="mdl-dialog__content">
<p>Enter a room name</p>
</div>
<div className="mdl-dialog__actions">
<form onSubmit={this.createRoom}>
<div className="mdl-textfield mdl-js-textfield">
<input type="text" value={this.state.name}
className="mdl-textfield__input" id="rooms" onChange=
{this.handleChange} />
<label className="mdl-textfield__label"
htmlFor="rooms">Enter room Name...</label>
<button type="submit" className="mdl-button submit-
close">Submit</button>
<button type="button" className="mdl-button submit-
close">Close</button>
</div>
</form>
</div>
</dialog>
</div>
);
return (
<div className="layout mdl-layout mdl-js-layout mdl-layout--
fixed-drawer mdl-layout--fixed-header">
<header className="header mdl-layout__header mdl-color--
grey-100 mdl-color-text--grey-600">
<div className="mdl-layout__header-row">
<span className="mdl-layout-title">Bloc Chat</span>
<div className="mdl-layout-spacer"></div>
</div>
</header>
<div className="drawer mdl-layout__drawer mdl-color--blue-
grey-900 mdl-color-text--blue-grey-50">
<header className="drawer-header">
<span>{newListForm}</span>
</header>
<nav className="navigation mdl-navigation mdl-color--
blue-grey-800">
<div>{roomlist}</div>
<div className="mdl-layout-spacer"></div>
</nav>
</div>
</div>
);
}
}
export default RoomList;
Here is a simple sample on how to build a modal with the new portal API provided from React v16.xx
Working demo can be found here. Just use the dropdown to navigate to the simple portal demo. A snapshot of the full code base can be found on github.
Working Code
import React, { Component } from "react";
import { createPortal } from "react-dom";
import "./simple-portal.css";
export default class SimplePortal extends Component {
constructor() {
super();
this.state = {
list: [],
input: "",
showDialog: false
};
this._onChange = this._onChange.bind(this);
this._onSubmit = this._onSubmit.bind(this);
}
_onChange(e) {
let input = e.target.value;
this.setState({ input });
}
_onSubmit(e) {
e.preventDefault();
let showDialog = false;
// Dont Mutate the State!!!
let list = this.state.list.slice();
list.push(this.state.input);
this.setState({ showDialog, list, input: "" });
}
render() {
const { showDialog, list, input } = this.state;
return (
<div className="container">
<div>
<button
className="btn"
onClick={e =>
this.setState({
showDialog: !showDialog
})
}
>
Add Item
</button>
</div>
{/* Render Items from List */}
<div>
<ul>
{list.map(item => {
return <li key={item}>{item}</li>;
})}
</ul>
</div>
{/* Show Modal - Renders Outside React Hierarchy Tree via Portal Pattern */}
{showDialog === true ? (
<DialogModal>
<div className="dialog-wrapper">
<h1>New List Item</h1>
<form onSubmit={this._onSubmit}>
<input type="text" value={input} onChange={this._onChange} />
</form>
</div>
</DialogModal>
) : null}
</div>
);
}
}
class DialogModal extends Component {
constructor() {
super();
this.body = document.getElementsByTagName("body")[0];
this.el = document.createElement("div");
this.el.id = "dialog-root";
}
componentDidMount() {
this.body.appendChild(this.el);
}
componentWillUnmount() {
this.body.removeChild(this.el);
}
render() {
return createPortal(this.props.children, this.el);
}
}
I don't see any event listeners on your buttons that would trigger a rendering of the modal. I would approach this by specifying an onClick event that would update the state which would render the modal/dialogue box.
Another solution, which may be the way you are thinking, is to have the state change to render the modal/dialogue box as visible from within the createRoom() function. Remember, updating state or getting new props will trigger a rendering of the component. You are trying to update the state to re-render your component with the modal/dialogue being shown.
Sorry if I misunderstood the question or your goal.