Skip to content
This repository has been archived by the owner on Jan 16, 2023. It is now read-only.

feat: Separating header click action from toggle click action. #229

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion __tests__/Container.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const renderComponent = (props = {}) => {
{...{decorators, onClick, animations}}
node={data}
style={style.tree.node}
onClick={onClick}
onClickToggle={onClick}
onClickHeader={onClick}
{...props}
/>);
wrapper.Toggle = () => wrapper.find('Toggle');
Expand All @@ -28,7 +31,11 @@ describe('<Container/>', () => {
expect(
wrapper
.children()
.contains(<decorators.Header node={data} style={style.tree.node.header}/>)
.contains(<decorators.Header
node={data}
style={style.tree.node.header}
onClick={onClick}
/>)
).toBe(true);
});
});
Expand Down
5 changes: 3 additions & 2 deletions example/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import PropTypes from 'prop-types';
import {Div} from '../src/components/common';

// Example: Customising The Header Decorator To Include Icons
const Header = ({style, node}) => {
const Header = ({style, node, onClick}) => {
const iconType = node.children ? 'folder' : 'file-text';
const iconClass = `fa fa-${iconType}`;
const iconStyle = {marginRight: '5px'};

return (
<Div style={style.base}>
<Div style={style.base} onClick={() => onClick()}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do onClick like this: onClick={onClick}

<Div style={style.title}>
<i className={iconClass} style={iconStyle}/>

Expand All @@ -23,6 +23,7 @@ const Header = ({style, node}) => {
Header.propTypes = {
node: PropTypes.object,
style: PropTypes.object,
onClick: PropTypes.func,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};

export default Header;
11 changes: 9 additions & 2 deletions example/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ class DemoTree extends PureComponent {
super(props);
this.state = {data};
this.onToggle = this.onToggle.bind(this);
this.onClickHeader = this.onClickHeader.bind(this);
}

onToggle(node, toggled) {
const {cursor, data} = this.state;
const {data, cursor} = this.state;

if (cursor) {
this.setState(() => ({cursor, active: false}));
Expand All @@ -28,7 +29,11 @@ class DemoTree extends PureComponent {
node.toggled = toggled;
}

this.setState(() => ({cursor: node, data: Object.assign({}, data)}));
this.setState(() => ({data: Object.assign({}, data)}));
}

onClickHeader(node) {
this.setState(() => ({cursor: node}));
}

onFilterMouseUp({target: {value}}) {
Expand Down Expand Up @@ -62,7 +67,9 @@ class DemoTree extends PureComponent {
<Treebeard
data={data}
decorators={{...decorators, Header}}
separateToggleEvent={true}
onToggle={this.onToggle}
onClickHeader={this.onClickHeader}
/>
</Div>
<Div style={styles.component}>
Expand Down
21 changes: 13 additions & 8 deletions src/components/Decorators/Container.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,22 @@ class Container extends PureComponent {
}

renderToggleDecorator() {
const {style, decorators} = this.props;
return <decorators.Toggle style={style.toggle}/>;
const {style, decorators, onClickToggle} = this.props;
return <decorators.Toggle style={style.toggle} onClick={onClickToggle ? () => onClickToggle() : null}/>;
}

render() {
const {style, decorators, terminal, onClick, node} = this.props;
const {style, decorators, terminal, onClick, onClickHeader, node} = this.props;
return (
<div
onClick={onClick}
onClick={onClick ? () => onClick() : null}
style={node.active ? {...style.container} : {...style.link}}
>
{!terminal ? this.renderToggle() : null}
<decorators.Header node={node} style={style.header}/>
<decorators.Header
node={node}
style={style.header}
onClick={onClickHeader} />
</div>
);
}
Expand All @@ -43,12 +46,14 @@ Container.propTypes = {
style: PropTypes.object.isRequired,
decorators: PropTypes.object.isRequired,
terminal: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
onClick: PropTypes.func,
onClickToggle: PropTypes.func,
onClickHeader: PropTypes.func,
animations: PropTypes.oneOfType([
PropTypes.object,
PropTypes.bool
PropTypes.bool,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

]).isRequired,
node: PropTypes.object.isRequired
node: PropTypes.object.isRequired,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};

export default Container;
7 changes: 4 additions & 3 deletions src/components/Decorators/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PropTypes from 'prop-types';

import {Div} from '../common';

const Header = ({node, style}) => (
<Div style={style.base}>
const Header = ({node, style, onClick}) => (
<Div style={style.base} onClick={onClick ? () => onClick() : null}>
<Div style={style.title}>
{node.name}
</Div>
Expand All @@ -13,7 +13,8 @@ const Header = ({node, style}) => (

Header.propTypes = {
style: PropTypes.object,
node: PropTypes.object.isRequired
node: PropTypes.object.isRequired,
onClick: PropTypes.func,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};

export default Header;
14 changes: 10 additions & 4 deletions src/components/Decorators/Toggle.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useCallback} from 'react';
import PropTypes from 'prop-types';
import styled from '@emotion/styled';

Expand All @@ -8,13 +8,18 @@ const Polygon = styled('polygon', {
shouldForwardProp: prop => ['className', 'children', 'points'].indexOf(prop) !== -1
})((({style}) => style));

const Toggle = ({style}) => {
const Toggle = ({onClick, style}) => {
const {height, width} = style;
const midHeight = height * 0.5;
const points = `0,0 0,${height} ${width},${midHeight}`;
const click = useCallback(() => {
if (onClick) {
onClick();
}
}, [onClick]);

return (
<Div style={style.base}>
<Div style={style.base} onClick={click}>
<Div style={style.wrapper}>
<svg {...{height, width}}>
<Polygon points={points} style={style.arrow}/>
Expand All @@ -25,7 +30,8 @@ const Toggle = ({style}) => {
};

Toggle.propTypes = {
style: PropTypes.object
style: PropTypes.object,
onClick: PropTypes.func,
};

export default Toggle;
8 changes: 5 additions & 3 deletions src/components/NodeHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class NodeHeader extends Component {
}

render() {
const {animations, decorators, node, onClick, style} = this.props;
const {animations, decorators, node, onClick, onClickToggle, onClickHeader, style} = this.props;
const {active, children} = node;
const terminal = !children;
let styles;
Expand All @@ -35,7 +35,7 @@ class NodeHeader extends Component {
}
return (
<decorators.Container
{...{animations, decorators, node, onClick, terminal}}
{...{animations, decorators, node, onClick, onClickToggle, onClickHeader, terminal}}
style={styles}
/>
);
Expand All @@ -50,7 +50,9 @@ NodeHeader.propTypes = {
PropTypes.bool
]).isRequired,
node: PropTypes.object.isRequired,
onClick: PropTypes.func
onClick: PropTypes.func,
onClickToggle: PropTypes.func,
onClickHeader: PropTypes.func,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};

export default NodeHeader;
55 changes: 46 additions & 9 deletions src/components/TreeNode/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ import Drawer from './Drawer';
import Loading from './Loading';

const Li = styled('li', {
shouldForwardProp: prop => ['className', 'children', 'ref'].indexOf(prop) !== -1
shouldForwardProp: prop => ['className', 'children', 'ref'].indexOf(prop) !== -1,
})(({style}) => style);

class TreeNode extends PureComponent {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove blank line

constructor() {
super();
this.onClick = this.onClick.bind(this);
this.onClickToggle = this.onClickToggle.bind(this);
this.onClickHeader = this.onClickHeader.bind(this);
}

onClick() {
const {node, onToggle} = this.props;
const {toggled} = node;
Expand All @@ -23,17 +31,32 @@ class TreeNode extends PureComponent {
}
}

onClickToggle() {
const {node, onToggle} = this.props;
if (onToggle) {
const {toggled} = node;
onToggle(node, !toggled);
}
}

onClickHeader() {
const {node, onClickHeader} = this.props;
if (onClickHeader) {
onClickHeader(node);
}
}

animations() {
const {animations, node} = this.props;
if (!animations) {
return {
toggle: defaultAnimations.toggle(this.props, 0)
toggle: defaultAnimations.toggle(this.props, 0),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};
}
const animation = Object.assign({}, animations, node.animations);
return {
toggle: animation.toggle(this.props),
drawer: animation.drawer(this.props)
drawer: animation.drawer(this.props),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};
}

Expand All @@ -45,7 +68,14 @@ class TreeNode extends PureComponent {
}

renderChildren(decorators) {
const {animations, decorators: propDecorators, node, style, onToggle} = this.props;
const {
animations, decorators: propDecorators,
node,
style,
separateToggleEvent,
onToggle,
onClickHeader,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

} = this.props;

if (node.loading) {
return (
Expand All @@ -62,7 +92,7 @@ class TreeNode extends PureComponent {
<Ul style={style.subtree}>
{children.map(child => (
<TreeNode
{...{onToggle, animations, style}}
{...{separateToggleEvent, onToggle, onClickHeader, animations, style}}
decorators={propDecorators}
key={child.id || randomString()}
node={child}
Expand All @@ -73,13 +103,18 @@ class TreeNode extends PureComponent {
}

render() {
const {node, style} = this.props;
const {node, style, separateToggleEvent} = this.props;
const decorators = this.decorators();
const animations = this.animations();
const {...restAnimationInfo} = animations.drawer;
return (
<Li style={style.base}>
<NodeHeader {...{decorators, animations, node, style}} onClick={() => this.onClick()}/>
<NodeHeader
{...{decorators, animations, node, style}}
onClick={separateToggleEvent ? null : () => this.onClick()}
onClickHeader={separateToggleEvent ? () => this.onClickHeader() : null}
onClickToggle={separateToggleEvent ? () => this.onClickToggle() : null}
/>
<Drawer restAnimationInfo={{...restAnimationInfo}}>
{node.toggled ? this.renderChildren(decorators, animations) : null}
</Drawer>
Expand All @@ -89,14 +124,16 @@ class TreeNode extends PureComponent {
}

TreeNode.propTypes = {
separateToggleEvent: PropTypes.bool,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you have props that are not required, you should add defaultProps

onToggle: PropTypes.func,
onClickHeader: PropTypes.func,
style: PropTypes.object.isRequired,
node: PropTypes.object.isRequired,
decorators: PropTypes.object.isRequired,
animations: PropTypes.oneOfType([
PropTypes.object,
PropTypes.bool
]).isRequired
PropTypes.bool,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

]).isRequired,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};

export default TreeNode;
2 changes: 1 addition & 1 deletion src/components/common/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import styled from '@emotion/styled';

export const Div = styled('div', {
shouldForwardProp: prop => ['className', 'children'].indexOf(prop) !== -1
shouldForwardProp: prop => ['className', 'children', 'onClick'].indexOf(prop) !== -1
})((({style}) => style));

export const Ul = styled('ul', {
Expand Down
8 changes: 5 additions & 3 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {Ul} from './common';
import defaultDecorators from './Decorators';
import TreeNode from './TreeNode';

const TreeBeard = ({animations, decorators, data, onToggle, style}) => (
const TreeBeard = ({animations, decorators, data, separateToggleEvent, onToggle, onClickHeader, style}) => (
<Ul style={{...defaultTheme.tree.base, ...style.tree.base}}>
{castArray(data).map(node => (
<TreeNode
{...{decorators, node, onToggle, animations}}
{...{decorators, node, separateToggleEvent, onToggle, onClickHeader, animations}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please create unit test when you press onClickHeader or when you have onClickHeader

key={node.id || randomString()}
style={{...defaultTheme.tree.node, ...style.tree.node}}
/>
Expand All @@ -31,14 +31,16 @@ TreeBeard.propTypes = {
PropTypes.object,
PropTypes.bool
]),
separateToggleEvent: PropTypes.bool,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add to defaultProps

onToggle: PropTypes.func,
onClickHeader: PropTypes.func,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add to defaultProps

decorators: PropTypes.object
};

TreeBeard.defaultProps = {
style: defaultTheme,
animations: defaultAnimations,
decorators: defaultDecorators
decorators: defaultDecorators,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comma

};

export default TreeBeard;