Skip to content
Snippets Groups Projects
Commit 39ba579d authored by Tom Robinson's avatar Tom Robinson
Browse files

Fix popovers closing when clicking outside

parent 817b6ade
No related branches found
No related tags found
No related merge requests found
......@@ -29,10 +29,10 @@ export default React.createClass({
}
},
handleClickOutside: function(e) {
handleClickOutside: function(...args) {
// only propagate event for the popover on top of the stack
if (this === popoverStack[popoverStack.length - 1]) {
this.props.handleClickOutside.apply(this, arguments);
this.props.handleClickOutside(...args);
}
},
......
'use strict';
/*global document*/
"use strict";
import OnClickOutsideWrapper from './OnClickOutsideWrapper.react'
import React, { Component, PropTypes } from "react";
import Tether from 'tether';
import OnClickOutsideWrapper from "./OnClickOutsideWrapper.react";
import Tether from "tether";
export default React.createClass({
displayName: 'Popover',
getDefaultProps: function() {
return {
isOpen: true
};
},
export default class Popover extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillMount: function() {
componentWillMount() {
this._popoverElement = document.createElement('span');
this._popoverElement.className = 'PopoverContainer';
this._popoverElement.id = Math.floor((Math.random() * 698754) + 1);
document.querySelector('body').appendChild(this._popoverElement);
},
}
componentDidMount: function() {
componentDidMount() {
this._renderPopover();
},
}
componentDidUpdate: function() {
componentDidUpdate() {
this._renderPopover();
},
}
componentWillUnmount: function() {
componentWillUnmount() {
if (this._tether) {
this._tether.destroy();
this._tether = undefined;
delete this._tether;
}
React.unmountComponentAtNode(this._popoverElement);
if (this._popoverElement.parentNode) {
this._popoverElement.parentNode.removeChild(this._popoverElement);
}
},
}
handleClickOutside: function() {
handleClickOutside(...args) {
if (this.props.onClose) {
this.props.onClose()
this.props.onClose(...args)
}
},
}
_popoverComponent: function() {
_popoverComponent() {
return (
<OnClickOutsideWrapper handleClickOutside={this.handleClickOutside}>
<OnClickOutsideWrapper handleClickOutside={this.handleClickOutside.bind(this)}>
<div className={this.props.className}>
{this.props.children}
</div>
</OnClickOutsideWrapper>
);
},
}
_tetherOptions: function() {
_tetherOptions() {
// sensible defaults for most popovers
return {
attachment: 'bottom right',
......@@ -66,18 +63,22 @@ export default React.createClass({
moveElement: false // always moves to <body> anyway!
}
};
},
}
_renderPopover: function() {
_renderPopover() {
if (this.props.isOpen) {
// modal is open, lets do this!
React.render(this._popoverComponent(), this._popoverElement);
// popover is open, lets do this!
React.render(
<div className="Popover-backdrop">
{this._popoverComponent()}
</div>
, this._popoverElement);
var tetherOptions = this.props.tetherOptions || this._tetherOptions();
// NOTE: these must be set here because they relate to OUR component and can't be passed in
tetherOptions.element = this._popoverElement;
tetherOptions.target = this.getDOMNode().parentNode;
tetherOptions.target = React.findDOMNode(this).parentNode;
if (this._tether !== undefined && this._tether !== null) {
this._tether.setOptions(tetherOptions);
......@@ -85,12 +86,21 @@ export default React.createClass({
this._tether = new Tether(tetherOptions);
}
} else {
// if the modal isn't open then actively unmount our popover
// if the popover isn't open then actively unmount our popover
React.unmountComponentAtNode(this._popoverElement);
}
},
}
render: function() {
render() {
return <span />;
}
});
}
Popover.propTypes = {
isOpen: PropTypes.bool
};
Popover.defaultProps = {
className: "Popover",
isOpen: true
};
......@@ -10,8 +10,7 @@ export default ComposedComponent => class extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: props.isInitiallyOpen || false,
recentlyToggled: false
isOpen: props.isInitiallyOpen || false
}
}
......@@ -24,20 +23,25 @@ export default ComposedComponent => class extends Component {
}
toggle(isOpen = !this.state.isOpen) {
if (!this.state.recentlyToggled) {
this.setState({ isOpen, recentlyToggled: true });
setTimeout(() => this.setState({ recentlyToggled: false }), 500);
this.setState({ isOpen });
}
onClose(e) {
// don't close if clicked the actual trigger, it will toggle
if (e && e.target && this.refs.trigger.getDOMNode().contains(e.target)) {
return;
}
this.close();
}
render() {
return (
<a href="#" onClick={() => this.toggle()} className={cx("no-decoration", this.props.triggerClasses)}>
<a ref="trigger" href="#" onClick={() => this.toggle()} className={cx("no-decoration", this.props.triggerClasses)}>
{this.props.triggerElement}
<ComposedComponent
{...this.props}
isOpen={this.state.isOpen}
onClose={() => this.close()}
onClose={this.onClose.bind(this)}
/>
</a>
);
......
......@@ -46,7 +46,7 @@ export default React.createClass({
ref="popover"
className="PopoverBody PopoverBody--withArrow FieldPopover"
tetherOptions={tetherOptions}
handleClickOutside={this.toggle}
onClose={this.toggle}
>
<FieldSelector
tableName={this.props.tableName}
......
......@@ -399,7 +399,7 @@ export default React.createClass({
className="PopoverBody PopoverBody--withArrow FilterPopover"
isInitiallyOpen={this.state.field === null}
tetherOptions={tetherOptions}
handleClickOutside={this.selectPane.bind(null, -1)}
onClose={this.selectPane.bind(null, -1)}
>
<ul className="PopoverHeader">
{tabs.map((t, index) => {
......
......@@ -44,7 +44,7 @@ export default React.createClass({
};
},
handleClickOutside: function() {
onClose: function() {
this.setState({
open: false,
expanded: false
......@@ -200,7 +200,7 @@ export default React.createClass({
<Popover
tetherOptions={tetherOptions}
className={"SelectionModule PopoverBody PopoverBody--withArrow " + this.props.className}
handleClickOutside={this.handleClickOutside}
onClose={this.onClose}
>
<div className={itemListClasses}>
{searchBar}
......
......@@ -163,7 +163,7 @@ export default React.createClass({
});
},
handleClickOutside: function() {
onClosePopover: function() {
this.setState({ popover: null });
},
......@@ -187,7 +187,7 @@ export default React.createClass({
popover = (
<Popover
tetherOptions={tetherOptions}
handleClickOutside={this.handleClickOutside}
onClose={this.onClosePopover}
>
<div className="bg-white bordered shadowed p1">
<ul className="h1 flex align-center">{operators}</ul>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment