web: components: Re-written GroupButton component to class style (issue: useState, useEffect break React functional components rules of usage)

This commit is contained in:
Alexey Safronov 2019-07-18 14:11:36 +03:00
parent f610230b2e
commit a84312376b
3 changed files with 147 additions and 104 deletions

View File

@ -1,146 +1,187 @@
import React, { useState, useEffect, useRef } from 'react'
import styled, { css } from 'styled-components'
import PropTypes from 'prop-types'
import { Icons } from '../icons'
import DropDown from '../drop-down'
import Checkbox from '../checkbox'
import React from "react";
import styled, { css } from "styled-components";
import PropTypes from "prop-types";
import { Icons } from "../icons";
import DropDown from "../drop-down";
import DropDownItem from "../drop-down-item";
const textColor = '#333333',
disabledTextColor = '#A3A9AE';
const textColor = "#333333",
disabledTextColor = "#A3A9AE";
const activatedCss = css`
cursor: pointer;
cursor: pointer;
`;
const hoveredCss = css`
cursor: pointer;
cursor: pointer;
`;
const StyledGroupButton = styled.div`
position: relative;
display: inline-flex;
vertical-align: middle;
position: relative;
display: inline-flex;
vertical-align: middle;
`;
const StyledDropdownToggle = styled.div`
font-family: Open Sans;
font-style: normal;
font-weight: ${props => props.fontWeight};
font-size: 14px;
line-height: 19px;
font-family: Open Sans;
font-style: normal;
font-weight: ${props => props.fontWeight};
font-size: 14px;
line-height: 19px;
cursor: default;
cursor: default;
color: ${props => (props.disabled ? disabledTextColor : textColor)};
float: left;
height: 19px;
margin: 18px 12px 19px 12px;
overflow: hidden;
padding: 0px;
color: ${props => (props.disabled ? disabledTextColor : textColor)};
text-align: center;
text-decoration: none;
white-space: nowrap;
float: left;
height: 19px;
margin: 18px 12px 19px 12px;
overflow: hidden;
padding: 0px;
user-select: none;
-o-user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
text-align: center;
text-decoration: none;
white-space: nowrap;
${props => !props.disabled && (props.activated ? `${activatedCss}` : css`
&:active {
user-select: none;
-o-user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
${props =>
!props.disabled &&
(props.activated
? `${activatedCss}`
: css`
&:active {
${activatedCss}
}`)
}
}
`)}
${props => !props.disabled && (props.hovered ? `${hoveredCss}` : css`
&:hover {
${props =>
!props.disabled &&
(props.hovered
? `${hoveredCss}`
: css`
&:hover {
${hoveredCss}
}`)
}
}
`)}
`;
const Caret = styled(Icons.ExpanderDownIcon)`
width: 10px;
margin-left: 4px;
width: 10px;
margin-left: 4px;
`;
const Separator = styled.div`
vertical-align: middle;
border: 0.5px solid #ECEEF1;
width: 1px;
height: 24px;
margin-top: 16px;
vertical-align: middle;
border: 0.5px solid #eceef1;
width: 1px;
height: 24px;
margin-top: 16px;
`;
const useOuterClickNotifier = (onOuterClick, ref) => {
useEffect(() => {
const handleClick = (e) => !ref.current.contains(e.target) && onOuterClick(e);
class GroupButton extends React.Component {
constructor(props) {
super(props);
if (ref.current) {
document.addEventListener("click", handleClick);
}
this.ref = React.createRef();
return () => document.removeEventListener("click", handleClick);
},
[onOuterClick, ref]
);
}
this.state = {
isOpen: props.opened
};
}
const GroupButton = (props) => {
const { label, isDropdown, opened, disabled, isSeparator} = props;
const [isOpen, toggle] = useState(opened);
onOuterClick = e => this.toggle(false);
handleClick = e =>
!this.ref.current.contains(e.target) && this.onOuterClick(e);
stopAction = e => e.preventDefault();
toggle = isOpen => this.setState({ isOpen: isOpen });
let ref = useRef(null);
componentDidMount() {
if (this.ref.current) {
document.addEventListener("click", this.handleClick);
}
}
useOuterClickNotifier(() => toggle(false), ref);
componentWillUnmount() {
document.removeEventListener("click", this.handleClick);
}
const dropMenu = <DropDown isOpen={isOpen} {...props}/>;
componentDidUpdate(prevProps) {
// Store prevId in state so we can compare when props change.
// Clear out previously-loaded data (so we don't render stale stuff).
if (this.props.opened !== prevProps.opened) {
this.toggle(this.props.opened);
}
}
const dropDownButton =
<StyledDropdownToggle {...props} onClick={() => { disabled ? false : toggle(!isOpen) }}>
{label}
<Caret size='small' color={disabled ? disabledTextColor : textColor} />
</StyledDropdownToggle>;
render() {
const { label, isDropdown, disabled, isSeparator } = this.props;
return (
<StyledGroupButton ref={ref}>
{isDropdown
? {...dropDownButton}
: <StyledDropdownToggle {...props} >
{label}
</StyledDropdownToggle>
<StyledGroupButton ref={this.ref}>
{isDropdown ? (
<>
<StyledDropdownToggle
{...this.props}
onClick={() => {
disabled ? false : this.toggle(!this.state.isOpen);
}}
>
{label}
<Caret
size="small"
color={disabled ? disabledTextColor : textColor}
/>
</StyledDropdownToggle>
<DropDown isOpen={this.state.isOpen}>
{
React.Children.map(this.props.children, (child) =>
<DropDownItem
{...child.props}
onClick={() => {
child.props.onClick && child.props.onClick();
this.toggle(!this.state.isOpen);
}}
/>
)
}
{isSeparator && <Separator/>}
{isDropdown && {...dropMenu}}
</StyledGroupButton>
</DropDown>
</>
) : (
<StyledDropdownToggle {...this.props}>{label}</StyledDropdownToggle>
)}
{isSeparator && <Separator />}
</StyledGroupButton>
);
}
}
GroupButton.propTypes = {
label: PropTypes.string,
disabled: PropTypes.bool,
activated: PropTypes.bool,
opened: PropTypes.bool,
hovered: PropTypes.bool,
isDropdown: PropTypes.bool,
isSeparator: PropTypes.bool,
tabIndex: PropTypes.number,
onClick: PropTypes.func,
fontWeight: PropTypes.string
label: PropTypes.string,
disabled: PropTypes.bool,
activated: PropTypes.bool,
opened: PropTypes.bool,
hovered: PropTypes.bool,
isDropdown: PropTypes.bool,
isSeparator: PropTypes.bool,
tabIndex: PropTypes.number,
onClick: PropTypes.func,
fontWeight: PropTypes.string
};
GroupButton.defaultProps = {
label: 'Group button',
disabled: false,
activated: false,
opened: false,
hovered: false,
isDropdown: false,
isSeparator: false,
tabIndex: -1,
fontWeight: '600'
label: "Group button",
disabled: false,
activated: false,
opened: false,
hovered: false,
isDropdown: false,
isSeparator: false,
tabIndex: -1,
fontWeight: "600"
};
export default GroupButton
export default GroupButton;

View File

@ -25,10 +25,10 @@ storiesOf('Components|GroupButton', module)
<Col><GroupButton disabled /></Col>
</Row>
<Row style={rowStyle}>
<Col><GroupButton isDropdown ><DropDownItem /></GroupButton></Col>
<Col><GroupButton isDropdown hovered ><DropDownItem /></GroupButton></Col>
<Col><GroupButton isDropdown activated ><DropDownItem /></GroupButton></Col>
<Col><GroupButton isDropdown disabled ><DropDownItem /></GroupButton></Col>
<Col><GroupButton isDropdown ><DropDownItem label="Action 1" /></GroupButton></Col>
<Col><GroupButton isDropdown hovered ><DropDownItem label="Action 2" /></GroupButton></Col>
<Col><GroupButton isDropdown activated ><DropDownItem label="Action 3" /></GroupButton></Col>
<Col><GroupButton isDropdown disabled ><DropDownItem label="Action 4" /></GroupButton></Col>
</Row>
</Container>
));

View File

@ -15,6 +15,8 @@ storiesOf('Components|GroupButton', module)
isDropdown = {boolean('isDropdown', false)}
opened = {boolean('opened', false)}
>
<DropDownItem/>
<DropDownItem label="Action 1" />
<DropDownItem label="Action 2" />
<DropDownItem label="Action 3" />
</GroupButton>
));