Skip to content

Commit bd12540

Browse files
committed
Refactors & modernizes all React components
Refactors Node to class properties Refactors Link to avoid pointless constructor Refactors Tree to class properties Removes unnecessary constructor in NodeWrapper Refactors remaining `PropTypes` -> `T`
1 parent 86d4e87 commit bd12540

File tree

8 files changed

+132
-164
lines changed

8 files changed

+132
-164
lines changed

src/Link/index.js

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
2+
import T from 'prop-types';
33
import { svg, select } from 'd3';
44

55
import './style.css';
66

77
export default class Link extends React.PureComponent {
8-
constructor(props) {
9-
super(props);
10-
this.state = {
11-
initialStyle: {
12-
opacity: 0,
13-
},
14-
};
15-
}
8+
state = {
9+
initialStyle: {
10+
opacity: 0,
11+
},
12+
};
1613

1714
componentDidMount() {
1815
this.applyOpacity(1, this.props.transitionDuration);
@@ -35,14 +32,14 @@ export default class Link extends React.PureComponent {
3532
}
3633
}
3734

38-
diagonalPath(linkData, orientation) {
35+
drawDiagonalPath(linkData, orientation) {
3936
const diagonal = svg
4037
.diagonal()
4138
.projection(d => (orientation === 'horizontal' ? [d.y, d.x] : [d.x, d.y]));
4239
return diagonal(linkData);
4340
}
4441

45-
straightPath(linkData, orientation) {
42+
drawStraightPath(linkData, orientation) {
4643
const straight = svg
4744
.line()
4845
.interpolate('basis')
@@ -64,7 +61,7 @@ export default class Link extends React.PureComponent {
6461
return straight(data);
6562
}
6663

67-
elbowPath(d, orientation) {
64+
drawElbowPath(d, orientation) {
6865
return orientation === 'horizontal'
6966
? `M${d.source.y},${d.source.x}V${d.target.x}H${d.target.y}`
7067
: `M${d.source.x},${d.source.y}V${d.target.y}H${d.target.x}`;
@@ -78,14 +75,14 @@ export default class Link extends React.PureComponent {
7875
}
7976

8077
if (pathFunc === 'elbow') {
81-
return this.elbowPath(linkData, orientation);
78+
return this.drawElbowPath(linkData, orientation);
8279
}
8380

8481
if (pathFunc === 'straight') {
85-
return this.straightPath(linkData, orientation);
82+
return this.drawStraightPath(linkData, orientation);
8683
}
8784

88-
return this.diagonalPath(linkData, orientation);
85+
return this.drawDiagonalPath(linkData, orientation);
8986
}
9087

9188
render() {
@@ -108,12 +105,9 @@ Link.defaultProps = {
108105
};
109106

110107
Link.propTypes = {
111-
linkData: PropTypes.object.isRequired,
112-
orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired,
113-
pathFunc: PropTypes.oneOfType([
114-
PropTypes.oneOf(['diagonal', 'elbow', 'straight']),
115-
PropTypes.func,
116-
]).isRequired,
117-
transitionDuration: PropTypes.number.isRequired,
118-
styles: PropTypes.object,
108+
linkData: T.object.isRequired,
109+
orientation: T.oneOf(['horizontal', 'vertical']).isRequired,
110+
pathFunc: T.oneOfType([T.oneOf(['diagonal', 'elbow', 'straight']), T.func]).isRequired,
111+
transitionDuration: T.number.isRequired,
112+
styles: T.object,
119113
};

src/Link/tests/index.test.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ describe('<Link />', () => {
3030
};
3131

3232
jest.spyOn(Link.prototype, 'drawPath');
33-
jest.spyOn(Link.prototype, 'diagonalPath');
34-
jest.spyOn(Link.prototype, 'elbowPath');
35-
jest.spyOn(Link.prototype, 'straightPath');
33+
jest.spyOn(Link.prototype, 'drawDiagonalPath');
34+
jest.spyOn(Link.prototype, 'drawElbowPath');
35+
jest.spyOn(Link.prototype, 'drawStraightPath');
3636
jest.spyOn(Link.prototype, 'applyOpacity');
3737
jest.spyOn(pathFuncs, 'testPathFunc');
3838

@@ -59,27 +59,27 @@ describe('<Link />', () => {
5959
const straightComponent = shallow(<Link {...mockProps} pathFunc="straight" />);
6060
shallow(<Link {...mockProps} pathFunc={pathFuncs.testPathFunc} />);
6161

62-
expect(diagonalComponent.instance().diagonalPath).toHaveBeenCalled();
63-
expect(elbowComponent.instance().elbowPath).toHaveBeenCalled();
64-
expect(straightComponent.instance().straightPath).toHaveBeenCalled();
62+
expect(diagonalComponent.instance().drawDiagonalPath).toHaveBeenCalled();
63+
expect(elbowComponent.instance().drawElbowPath).toHaveBeenCalled();
64+
expect(straightComponent.instance().drawStraightPath).toHaveBeenCalled();
6565
expect(pathFuncs.testPathFunc).toHaveBeenCalled();
6666
expect(Link.prototype.drawPath).toHaveBeenCalledTimes(4);
6767
});
6868

6969
it('returns an appropriate elbowPath according to `props.orientation`', () => {
70-
expect(Link.prototype.elbowPath(linkData, 'horizontal')).toBe(
70+
expect(Link.prototype.drawElbowPath(linkData, 'horizontal')).toBe(
7171
`M${linkData.source.y},${linkData.source.x}V${linkData.target.x}H${linkData.target.y}`,
7272
);
73-
expect(Link.prototype.elbowPath(linkData, 'vertical')).toBe(
73+
expect(Link.prototype.drawElbowPath(linkData, 'vertical')).toBe(
7474
`M${linkData.source.x},${linkData.source.y}V${linkData.target.y}H${linkData.target.x}`,
7575
);
7676
});
7777

7878
it('returns an appropriate straightPath according to `props.orientation`', () => {
79-
expect(Link.prototype.straightPath(linkData, 'horizontal')).toBe(
79+
expect(Link.prototype.drawStraightPath(linkData, 'horizontal')).toBe(
8080
`M${linkData.source.y},${linkData.source.x}L${linkData.target.y},${linkData.target.x}`,
8181
);
82-
expect(Link.prototype.straightPath(linkData, 'vertical')).toBe(
82+
expect(Link.prototype.drawStraightPath(linkData, 'vertical')).toBe(
8383
`M${linkData.source.x},${linkData.source.y}L${linkData.target.x},${linkData.target.y}`,
8484
);
8585
});

src/Node/ForeignObjectElement.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
2+
import T from 'prop-types';
33

44
export const BASE_MARGIN = 24;
55

@@ -23,11 +23,11 @@ ForeignObjectElement.defaultProps = {
2323
};
2424

2525
ForeignObjectElement.propTypes = {
26-
render: PropTypes.oneOfType([PropTypes.element, PropTypes.node]).isRequired,
27-
nodeData: PropTypes.object.isRequired,
28-
nodeSize: PropTypes.shape({
29-
x: PropTypes.number,
30-
y: PropTypes.number,
26+
render: T.oneOfType([T.element, T.node]).isRequired,
27+
nodeData: T.object.isRequired,
28+
nodeSize: T.shape({
29+
x: T.number,
30+
y: T.number,
3131
}).isRequired,
32-
foreignObjectWrapper: PropTypes.object,
32+
foreignObjectWrapper: T.object,
3333
};

src/Node/SvgTextElement.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import uuid from 'uuid';
3-
import PropTypes from 'prop-types';
3+
import T from 'prop-types';
44

55
export default class SvgTextElement extends React.PureComponent {
66
render() {
@@ -42,8 +42,8 @@ SvgTextElement.defaultProps = {
4242
};
4343

4444
SvgTextElement.propTypes = {
45-
name: PropTypes.string.isRequired,
46-
attributes: PropTypes.object,
47-
textLayout: PropTypes.object.isRequired,
48-
nodeStyle: PropTypes.object.isRequired,
45+
name: T.string.isRequired,
46+
attributes: T.object,
47+
textLayout: T.object.isRequired,
48+
nodeStyle: T.object.isRequired,
4949
};

src/Node/index.js

Lines changed: 52 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,50 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
2+
import T from 'prop-types';
33
import { select } from 'd3';
44

5-
import './style.css';
65
import SvgTextElement from './SvgTextElement';
76
import ForeignObjectElement from './ForeignObjectElement';
7+
import './style.css';
88

99
export default class Node extends React.Component {
10-
constructor(props) {
11-
super(props);
12-
const { nodeData: { parent }, orientation } = props;
13-
const originX = parent ? parent.x : 0;
14-
const originY = parent ? parent.y : 0;
15-
16-
this.state = {
17-
transform: this.setTransformOrientation(originX, originY, orientation),
18-
initialStyle: {
19-
opacity: 0,
20-
},
21-
};
22-
23-
this.handleClick = this.handleClick.bind(this);
24-
this.handleOnMouseOver = this.handleOnMouseOver.bind(this);
25-
this.handleOnMouseOut = this.handleOnMouseOut.bind(this);
26-
}
10+
state = {
11+
transform: this.setTransform(this.props.nodeData, this.props.orientation, true),
12+
initialStyle: {
13+
opacity: 0,
14+
},
15+
};
2716

2817
componentDidMount() {
29-
const { nodeData: { x, y }, orientation, transitionDuration } = this.props;
30-
const transform = this.setTransformOrientation(x, y, orientation);
18+
const { nodeData, orientation, transitionDuration } = this.props;
19+
const transform = this.setTransform(nodeData, orientation);
3120

3221
this.applyTransform(transform, transitionDuration);
3322
}
3423

3524
componentWillUpdate(nextProps) {
36-
const transform = this.setTransformOrientation(
37-
nextProps.nodeData.x,
38-
nextProps.nodeData.y,
39-
nextProps.orientation,
40-
);
25+
const transform = this.setTransform(nextProps.nodeData, nextProps.orientation);
4126
this.applyTransform(transform, nextProps.transitionDuration);
4227
}
4328

4429
shouldComponentUpdate(nextProps) {
4530
return this.shouldNodeTransform(this.props, nextProps);
4631
}
4732

48-
shouldNodeTransform(ownProps, nextProps) {
49-
return (
50-
nextProps.subscriptions !== ownProps.subscriptions ||
51-
nextProps.nodeData.x !== ownProps.nodeData.x ||
52-
nextProps.nodeData.y !== ownProps.nodeData.y ||
53-
nextProps.orientation !== ownProps.orientation
54-
);
55-
}
56-
57-
setTransformOrientation(x, y, orientation) {
33+
shouldNodeTransform = (ownProps, nextProps) =>
34+
nextProps.subscriptions !== ownProps.subscriptions ||
35+
nextProps.nodeData.x !== ownProps.nodeData.x ||
36+
nextProps.nodeData.y !== ownProps.nodeData.y ||
37+
nextProps.orientation !== ownProps.orientation;
38+
39+
setTransform(nodeData, orientation, shouldTranslateToOrigin = false) {
40+
const { x, y, parent } = nodeData;
41+
if (shouldTranslateToOrigin) {
42+
const originX = parent ? parent.x : 0;
43+
const originY = parent ? parent.y : 0;
44+
return orientation === 'horizontal'
45+
? `translate(${originY},${originX})`
46+
: `translate(${originX},${originY})`;
47+
}
5848
return orientation === 'horizontal' ? `translate(${y},${x})` : `translate(${x},${y})`;
5949
}
6050

@@ -74,7 +64,7 @@ export default class Node extends React.Component {
7464
}
7565
}
7666

77-
renderNodeElement(nodeStyle) {
67+
renderNodeElement = nodeStyle => {
7868
const { circleRadius, nodeSvgShape } = this.props;
7969
/* TODO: DEPRECATE <circle /> */
8070
if (circleRadius) {
@@ -87,26 +77,23 @@ export default class Node extends React.Component {
8777
...nodeStyle.circle,
8878
...nodeSvgShape.shapeProps,
8979
});
90-
}
80+
};
9181

92-
handleClick(evt) {
82+
handleClick = evt => {
9383
this.props.onClick(this.props.nodeData.id, evt);
94-
}
84+
};
9585

96-
handleOnMouseOver(evt) {
86+
handleOnMouseOver = evt => {
9787
this.props.onMouseOver(this.props.nodeData.id, evt);
98-
}
88+
};
9989

100-
handleOnMouseOut(evt) {
90+
handleOnMouseOut = evt => {
10191
this.props.onMouseOut(this.props.nodeData.id, evt);
102-
}
92+
};
10393

10494
componentWillLeave(done) {
105-
const { nodeData: { parent }, orientation, transitionDuration } = this.props;
106-
const originX = parent ? parent.x : 0;
107-
const originY = parent ? parent.y : 0;
108-
const transform = this.setTransformOrientation(originX, originY, orientation);
109-
95+
const { nodeData, orientation, transitionDuration } = this.props;
96+
const transform = this.setTransform(nodeData, orientation, true);
11097
this.applyTransform(transform, transitionDuration, 0, done);
11198
}
11299

@@ -157,20 +144,20 @@ Node.defaultProps = {
157144
};
158145

159146
Node.propTypes = {
160-
nodeData: PropTypes.object.isRequired,
161-
nodeSvgShape: PropTypes.object.isRequired,
162-
nodeLabelComponent: PropTypes.object,
163-
nodeSize: PropTypes.object.isRequired,
164-
orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired,
165-
transitionDuration: PropTypes.number.isRequired,
166-
onClick: PropTypes.func.isRequired,
167-
onMouseOver: PropTypes.func.isRequired,
168-
onMouseOut: PropTypes.func.isRequired,
169-
name: PropTypes.string.isRequired,
170-
attributes: PropTypes.object,
171-
textLayout: PropTypes.object.isRequired,
172-
subscriptions: PropTypes.object.isRequired, // eslint-disable-line react/no-unused-prop-types
173-
allowForeignObjects: PropTypes.bool.isRequired,
174-
circleRadius: PropTypes.number,
175-
styles: PropTypes.object,
147+
nodeData: T.object.isRequired,
148+
nodeSvgShape: T.object.isRequired,
149+
nodeLabelComponent: T.object,
150+
nodeSize: T.object.isRequired,
151+
orientation: T.oneOf(['horizontal', 'vertical']).isRequired,
152+
transitionDuration: T.number.isRequired,
153+
onClick: T.func.isRequired,
154+
onMouseOver: T.func.isRequired,
155+
onMouseOut: T.func.isRequired,
156+
name: T.string.isRequired,
157+
attributes: T.object,
158+
textLayout: T.object.isRequired,
159+
subscriptions: T.object.isRequired, // eslint-disable-line react/no-unused-prop-types
160+
allowForeignObjects: T.bool.isRequired,
161+
circleRadius: T.number,
162+
styles: T.object,
176163
};

src/Tree/NodeWrapper.js

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
2+
import T from 'prop-types';
33
import { TransitionGroup } from 'react-transition-group';
44

55
export default class NodeWrapper extends React.Component {
6-
constructor(props) {
7-
super(props);
8-
this.state = {
9-
enableTransitions: props.transitionDuration > 0,
10-
};
11-
}
6+
state = {
7+
enableTransitions: this.props.transitionDuration > 0,
8+
};
129

1310
componentWillReceiveProps(nextProps) {
1411
if (nextProps.transitionDuration !== this.props.transitionDuration) {
@@ -44,9 +41,9 @@ NodeWrapper.defaultProps = {
4441
};
4542

4643
NodeWrapper.propTypes = {
47-
transitionDuration: PropTypes.number.isRequired,
48-
component: PropTypes.string,
49-
className: PropTypes.string.isRequired,
50-
transform: PropTypes.string.isRequired,
51-
children: PropTypes.array.isRequired,
44+
transitionDuration: T.number.isRequired,
45+
component: T.string,
46+
className: T.string.isRequired,
47+
transform: T.string.isRequired,
48+
children: T.array.isRequired,
5249
};

0 commit comments

Comments
 (0)