* Add KuiOutsideClickDetector. * Convert KuiColorPicker and KuiPopover to use KuiOutsideClickDetector.
This commit is contained in:
parent
d5ce2e60fb
commit
e087c4b7d9
|
@ -1,9 +1,10 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ChromePicker } from 'react-color';
|
||||
|
||||
import { KuiOutsideClickDetector } from '../outside_click_detector';
|
||||
|
||||
import { KuiColorPickerSwatch } from './color_picker_swatch';
|
||||
|
||||
export class KuiColorPicker extends React.Component {
|
||||
|
@ -12,16 +13,9 @@ export class KuiColorPicker extends React.Component {
|
|||
this.state = {
|
||||
showColorSelector: false,
|
||||
};
|
||||
// Use this variable to differentiate between clicks on the element that should not cause the pop up
|
||||
// to close, and external clicks that should cause the pop up to close.
|
||||
this.clickedMyself = false;
|
||||
}
|
||||
|
||||
closeColorSelector = () => {
|
||||
if (this.clickedMyself) {
|
||||
this.clickedMyself = false;
|
||||
return;
|
||||
}
|
||||
this.setState({ showColorSelector: false });
|
||||
};
|
||||
|
||||
|
@ -33,21 +27,6 @@ export class KuiColorPicker extends React.Component {
|
|||
this.props.onChange(color.hex);
|
||||
};
|
||||
|
||||
onClickRootElement = () => {
|
||||
// This prevents clicking on the element from closing it, due to the event handler on the
|
||||
// document object.
|
||||
this.clickedMyself = true;
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// When the user clicks somewhere outside of the color picker, we will dismiss it.
|
||||
document.addEventListener('click', this.closeColorSelector);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.closeColorSelector);
|
||||
}
|
||||
|
||||
getColorLabel() {
|
||||
const { color } = this.props;
|
||||
const colorValue = color === null ? '(transparent)' : color;
|
||||
|
@ -65,30 +44,31 @@ export class KuiColorPicker extends React.Component {
|
|||
const { color, className, showColorLabel } = this.props;
|
||||
const classes = classNames('kuiColorPicker', className);
|
||||
return (
|
||||
<div
|
||||
className={classes}
|
||||
data-test-subj={this.props['data-test-subj']}
|
||||
onClick={this.onClickRootElement}
|
||||
>
|
||||
<KuiOutsideClickDetector onOutsideClick={this.closeColorSelector}>
|
||||
<div
|
||||
className="kuiColorPicker__preview"
|
||||
onClick={this.toggleColorSelector}
|
||||
className={classes}
|
||||
data-test-subj={this.props['data-test-subj']}
|
||||
>
|
||||
<KuiColorPickerSwatch color={color} aria-label={this.props['aria-label']} />
|
||||
{ showColorLabel ? this.getColorLabel() : null }
|
||||
<div
|
||||
className="kuiColorPicker__preview"
|
||||
onClick={this.toggleColorSelector}
|
||||
>
|
||||
<KuiColorPickerSwatch color={color} aria-label={this.props['aria-label']} />
|
||||
{ showColorLabel ? this.getColorLabel() : null }
|
||||
</div>
|
||||
{
|
||||
this.state.showColorSelector ?
|
||||
<div className="kuiColorPickerPopUp" data-test-subj="colorPickerPopup">
|
||||
<ChromePicker
|
||||
color={color ? color : '#ffffff'}
|
||||
disableAlpha={true}
|
||||
onChange={this.handleColorSelection}
|
||||
/>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
{
|
||||
this.state.showColorSelector ?
|
||||
<div className="kuiColorPickerPopUp" data-test-subj="colorPickerPopup">
|
||||
<ChromePicker
|
||||
color={color ? color : '#ffffff'}
|
||||
disableAlpha={true}
|
||||
onChange={this.handleColorSelection}
|
||||
/>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
</KuiOutsideClickDetector>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,10 @@ export {
|
|||
KuiModalOverlay,
|
||||
} from './modal';
|
||||
|
||||
export {
|
||||
KuiOutsideClickDetector,
|
||||
} from './outside_click_detector';
|
||||
|
||||
export {
|
||||
KuiPager,
|
||||
KuiPagerButtonGroup,
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KuiOutsideClickDetector is rendered 1`] = `<div />`;
|
|
@ -0,0 +1,3 @@
|
|||
export {
|
||||
KuiOutsideClickDetector,
|
||||
} from './outside_click_detector';
|
|
@ -0,0 +1,48 @@
|
|||
import {
|
||||
Children,
|
||||
cloneElement,
|
||||
Component,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export class KuiOutsideClickDetector extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
onOutsideClick: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
onClickOutside = event => {
|
||||
if (!this.wrapperRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.wrapperRef === event.target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.wrapperRef.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onOutsideClick();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('click', this.onClickOutside);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.onClickOutside);
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = Object.assign({}, this.props.children.props, {
|
||||
ref: node => {
|
||||
this.wrapperRef = node;
|
||||
},
|
||||
});
|
||||
|
||||
const child = Children.only(this.props.children);
|
||||
return cloneElement(child, props);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
|
||||
import { KuiOutsideClickDetector } from './outside_click_detector';
|
||||
|
||||
describe('KuiOutsideClickDetector', () => {
|
||||
test('is rendered', () => {
|
||||
const component = render(
|
||||
<KuiOutsideClickDetector onOutsideClick={() => {}}>
|
||||
<div />
|
||||
</KuiOutsideClickDetector>
|
||||
);
|
||||
|
||||
expect(component)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,7 +1,9 @@
|
|||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { KuiOutsideClickDetector } from '../outside_click_detector';
|
||||
|
||||
const anchorPositionToClassNameMap = {
|
||||
'center': '',
|
||||
'left': 'kuiPopover--anchorLeft',
|
||||
|
@ -10,80 +12,45 @@ const anchorPositionToClassNameMap = {
|
|||
|
||||
export const ANCHOR_POSITIONS = Object.keys(anchorPositionToClassNameMap);
|
||||
|
||||
export class KuiPopover extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
export const KuiPopover = ({
|
||||
anchorPosition,
|
||||
bodyClassName,
|
||||
button,
|
||||
isOpen,
|
||||
children,
|
||||
className,
|
||||
closePopover,
|
||||
...rest,
|
||||
}) => {
|
||||
const classes = classNames(
|
||||
'kuiPopover',
|
||||
anchorPositionToClassNameMap[anchorPosition],
|
||||
className,
|
||||
{
|
||||
'kuiPopover-isOpen': isOpen,
|
||||
},
|
||||
);
|
||||
|
||||
// Use this variable to differentiate between clicks on the element that should not cause the pop up
|
||||
// to close, and external clicks that should cause the pop up to close.
|
||||
this.didClickMyself = false;
|
||||
}
|
||||
const bodyClasses = classNames('kuiPopover__body', bodyClassName);
|
||||
|
||||
closePopover = () => {
|
||||
if (this.didClickMyself) {
|
||||
this.didClickMyself = false;
|
||||
return;
|
||||
}
|
||||
const body = (
|
||||
<div className={bodyClasses}>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
|
||||
this.props.closePopover();
|
||||
};
|
||||
|
||||
onClickRootElement = () => {
|
||||
// This prevents clicking on the element from closing it, due to the event handler on the
|
||||
// document object.
|
||||
this.didClickMyself = true;
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// When the user clicks somewhere outside of the color picker, we will dismiss it.
|
||||
document.addEventListener('click', this.closePopover);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('click', this.closePopover);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
anchorPosition,
|
||||
bodyClassName,
|
||||
button,
|
||||
isOpen,
|
||||
children,
|
||||
className,
|
||||
closePopover, // eslint-disable-line no-unused-vars
|
||||
...rest,
|
||||
} = this.props;
|
||||
|
||||
const classes = classNames(
|
||||
'kuiPopover',
|
||||
anchorPositionToClassNameMap[anchorPosition],
|
||||
className,
|
||||
{
|
||||
'kuiPopover-isOpen': isOpen,
|
||||
},
|
||||
);
|
||||
|
||||
const bodyClasses = classNames('kuiPopover__body', bodyClassName);
|
||||
|
||||
const body = (
|
||||
<div className={bodyClasses}>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
return (
|
||||
<KuiOutsideClickDetector onOutsideClick={closePopover}>
|
||||
<div
|
||||
onClick={this.onClickRootElement}
|
||||
className={classes}
|
||||
{...rest}
|
||||
>
|
||||
{ button }
|
||||
{ body }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
</KuiOutsideClickDetector>
|
||||
);
|
||||
};
|
||||
|
||||
KuiPopover.propTypes = {
|
||||
isOpen: PropTypes.bool,
|
||||
|
|
Loading…
Reference in a new issue