Skip to content

Commit beeebc9

Browse files
authored
Refactor/improve code structure (#35)
* Move validateGraphData to Graph/helper.jsx * Improve docs for validateGraphData * Make node symbols constants shared accross components * Move initializeGraphState to Graph/helper * Update docs. Use destructuring for method initializeGraphState * Update graph/config docs * Small 🐛 fix * Use lowercase naming for components * Add docs lint task in check npm script * tmp directories * Revert tmp directories
1 parent 0470667 commit beeebc9

File tree

15 files changed

+128
-104
lines changed

15 files changed

+128
-104
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"author": "Daniel Caldas",
66
"license": "MIT",
77
"scripts": {
8-
"check": "npm run lint && npm run test",
8+
"check": "npm run docs:lint && npm run lint && npm run test",
99
"dev": "node_modules/.bin/webpack-dev-server -d --content-base sandbox --inline --hot --port 3002",
1010
"dist": "npm run check && node_modules/.bin/npm-run-all --parallel dist:*",
1111
"dist:rd3g": "rm -rf dist/ && webpack --config webpack.config.dist.js -p --display-modules",

sandbox/Sandbox.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Form from 'react-jsonschema-form';
44

55
import './styles.css';
66

7-
import defaultConfig from '../src/components/Graph/config';
7+
import defaultConfig from '../src/components/graph/config';
88
import { Graph } from '../src';
99
import data from './data';
1010
import Utils from './utils';
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* highlighted. If the value is set to **1** the selected node and his 1st degree connections will be highlighted. If
3838
* the value is set to **2** the selected node will be highlighted as well as the 1st and 2nd common degree connections.
3939
* @param {number} [highlightOpacity=1] - this value is used to highlight nodes in the network. The lower
40-
* the value the more the less highlighted nodes will be visible (related to **highlightBehavior**).
40+
* the value the more the less highlighted nodes will be visible (related to *highlightBehavior*).
4141
* @param {number} [maxZoom=8] - max zoom that can be performed against the graph.
4242
* @param {number} [minZoom=0.1] - min zoom that can be performed against the graph.
4343
* @param {boolean} [panAndZoom=false] - 🚅🚅🚅 pan and zoom effect when performing zoom in the graph,
@@ -48,7 +48,7 @@
4848
* from the given nodes positions by rd3g), no coordinates will be calculated by rd3g or subjacent d3 modules.
4949
* @param {number} [width=800] - the width of the (svg) area where the graph will be rendered.
5050
* <br/>
51-
* @param {Object} node node object is explained in next section.
51+
* @param {Object} node node object is explained in next section. ⬇️
5252
* <h2 id="node-section">Node level configurations</h2>
5353
* @param {string} [node.color='#d3d3d3'] - 🔍🔍🔍 this is the color that will be applied to the node if no **color property**
5454
* is found inside the node itself (yes **you can pass a property 'color' inside the node and that color will override the
@@ -78,15 +78,15 @@
7878
* - 'triangle'
7979
* - 'wye'
8080
*
81-
* **[note]** react-d3-graph will map this values to d3 symbols ({@link https://github.com/d3/d3-shape#symbols})
81+
* **[note]** react-d3-graph will map this values to [d3 symbols](https://github.com/d3/d3-shape#symbols)
8282
* @param {string} [node.highlightColor='SAME'] - color for all highlighted nodes (use string 'SAME' if you
8383
* want the node to keep its color in highlighted state).
84-
* @param {number} [node.highlightFontSize=10] - node.fontSize in highlighted state.
85-
* @param {string} [node.highlightFontWeight='normal'] - node.fontWeight in highlighted state.
86-
* @param {string} [node.highlightStrokeColor='SAME'] - node.strokeColor in highlighted state.
87-
* @param {number} [node.highlightStrokeWidth=1.5] - node.strokeWidth in highlighted state.
84+
* @param {number} [node.highlightFontSize=10] - fontSize in highlighted state.
85+
* @param {string} [node.highlightFontWeight='normal'] - fontWeight in highlighted state.
86+
* @param {string} [node.highlightStrokeColor='SAME'] - strokeColor in highlighted state.
87+
* @param {number} [node.highlightStrokeWidth=1.5] - strokeWidth in highlighted state.
8888
* <br/>
89-
* @param {Object} link link object is explained in the next section.
89+
* @param {Object} link link object is explained in the next section. ⬇️
9090
* <h2>Link level configurations</h2>
9191
* @param {string} [link.color='#d3d3d3'] - the color for links.
9292
* @param {number} [link.opacity=1] - the default opacity value for links.
@@ -97,7 +97,7 @@
9797
* strokeWidth += (linkValue * strokeWidth) / 10;
9898
* ```
9999
* @param {number} [link.strokeWidth=1.5] - strokeWidth for all links.
100-
* @param {string} [link.highlightColor='#d3d3d3'] - links color in highlight state.
100+
* @param {string} [link.highlightColor='#d3d3d3'] - links' color in highlight state.
101101
*
102102
* @example
103103
* // A simple config that uses some properties
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import CONST from '../../const';
2+
13
export default {
24
COORDS_SEPARATOR: ',',
35
FORCE_IDEAL_STRENGTH: -100, // @TODO: Expose as configurable,
@@ -10,13 +12,5 @@ export default {
1012
},
1113
LINK_CLASS_NAME: 'link',
1214
NODE_CLASS_NAME: 'node',
13-
SYMBOLS: { // FIXME: Repeated SYMBOLS constant
14-
CIRCLE: 'circle',
15-
CROSS: 'cross',
16-
DIAMOND: 'diamond',
17-
SQUARE: 'square',
18-
STAR: 'star',
19-
TRIANGLE: 'triangle',
20-
WYE: 'wye'
21-
}
15+
...CONST
2216
};
Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33
* @description
44
* Offers a series of methods that isolate logic of Graph component.
55
*/
6+
/**
7+
* @typedef {Object} Link
8+
* @property {string} source - the node id of the source in the link.
9+
* @property {string} target - the node id of the target in the link.
10+
* @memberof Graph/helper
11+
*/
12+
/**
13+
* @typedef {Object} Node
14+
* @property {string} id - the id of the node.
15+
* @memberof Graph/helper
16+
*/
617
import React from 'react';
718

819
import {
@@ -13,9 +24,12 @@ import {
1324
} from 'd3-force';
1425

1526
import CONST from './const';
27+
import DEFAULT_CONFIG from './config';
28+
import ERRORS from '../../err';
1629

17-
import Link from '../Link/';
18-
import Node from '../Node/';
30+
import Link from '../link/';
31+
import Node from '../node/';
32+
import utils from '../../utils';
1933

2034
/**
2135
* Build some Link properties based on given parameters.
@@ -285,6 +299,59 @@ function createForceSimulation(width, height) {
285299
.force('y', fry);
286300
}
287301

302+
/**
303+
* Incapsulates common procedures to initialize graph.
304+
* @param {Object} props - Graph component props, object that holds data, id and config.
305+
* @param {Object} props.data - Data object holds links (array of **Link**) and nodes (array of **Node**).
306+
* @param {string} props.id - the graph id.
307+
* @param {Object} props.config - same as {@link #buildGraph|config in buildGraph}.
308+
* @param {Object} state - Graph component current state (same format as returned object on this function).
309+
* @returns a fully (re)initialized graph state object.
310+
* @memberof Graph/helper
311+
*/
312+
function initializeGraphState({data, id, config}, state) {
313+
let graph;
314+
315+
validateGraphData(data);
316+
317+
if (state && state.nodes && state.links && state.nodeIndexMapping) {
318+
// absorve existent positining
319+
graph = {
320+
nodes: data.nodes.map(n => Object.assign({}, n, state.nodes[n.id])),
321+
links: {}
322+
};
323+
} else {
324+
graph = {
325+
nodes: data.nodes.map(n => Object.assign({}, n)),
326+
links: {}
327+
};
328+
}
329+
330+
graph.links = data.links.map(l => Object.assign({}, l));
331+
332+
let newConfig = Object.assign({}, utils.merge(DEFAULT_CONFIG, config || {}));
333+
let {nodes, nodeIndexMapping} = initializeNodes(graph.nodes);
334+
let links = initializeLinks(graph.links); // Matrix of graph connections
335+
const {nodes: d3Nodes, links: d3Links} = graph;
336+
const formatedId = id.replace(/ /g, '_');
337+
const simulation = createForceSimulation(newConfig.width, newConfig.height);
338+
339+
return {
340+
id: formatedId,
341+
config: newConfig,
342+
nodeIndexMapping,
343+
links,
344+
d3Links,
345+
nodes,
346+
d3Nodes,
347+
highlightedNode: '',
348+
simulation,
349+
newGraphElements: false,
350+
configUpdated: false,
351+
transform: 1
352+
};
353+
}
354+
288355
/**
289356
* Receives a matrix of the graph with the links source and target as concrete node instances and it transforms it
290357
* in a lightweight matrix containing only links with source and target being strings representative of some node id
@@ -345,9 +412,27 @@ function initializeNodes(graphNodes) {
345412
return { nodes, nodeIndexMapping };
346413
}
347414

415+
/**
416+
* Some integraty validations on links and nodes structure. If some validation fails the function will
417+
* throw an error.
418+
* @param {Object} data - Same as {@link #initializeGraphState|data in initializeGraphState}.
419+
* @memberof Graph/helper
420+
*/
421+
function validateGraphData(data) {
422+
data.links.forEach(l => {
423+
if (!data.nodes.find(n => n.id === l.source)) {
424+
utils.throwErr(this.constructor.name, `${ERRORS.INVALID_LINKS} - ${l.source} is not a valid node id`);
425+
}
426+
if (!data.nodes.find(n => n.id === l.target)) {
427+
utils.throwErr(this.constructor.name, `${ERRORS.INVALID_LINKS} - ${l.target} is not a valid node id`);
428+
}
429+
});
430+
}
431+
348432
export default {
349433
buildGraph,
350434
createForceSimulation,
435+
initializeGraphState,
351436
initializeLinks,
352437
initializeNodes
353438
};
Lines changed: 3 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export default class Graph extends React.Component {
190190

191191
/**
192192
* This method resets all nodes fixed positions by deleting the properties fx (fixed x)
193-
* and fy (fixed y). Next a simulation is triggered in order to force nodes to go back
193+
* and fy (fixed y). Following this, a simulation is triggered in order to force nodes to go back
194194
* to their original positions (or at least new positions according to the d3 force parameters).
195195
*/
196196
resetNodesPositions = () => {
@@ -216,72 +216,6 @@ export default class Graph extends React.Component {
216216
*/
217217
restartSimulation = () => !this.state.config.staticGraph && this.state.simulation.restart();
218218

219-
/**
220-
* Some integraty validations on links and nodes structure.
221-
* @param {Object} data
222-
*/
223-
_validateGraphData(data) {
224-
// @TODO: Move function to helper.jsx
225-
data.links.forEach(l => {
226-
if (!data.nodes.find(n => n.id === l.source)) {
227-
utils.throwErr(this.constructor.name, `${ERRORS.INVALID_LINKS} - ${l.source} is not a valid node id`);
228-
}
229-
if (!data.nodes.find(n => n.id === l.target)) {
230-
utils.throwErr(this.constructor.name, `${ERRORS.INVALID_LINKS} - ${l.target} is not a valid node id`);
231-
}
232-
});
233-
}
234-
235-
/**
236-
* Incapsulates common procedures to initialize graph.
237-
* @param {Object} data
238-
* @param {Array.<Object>} data.nodes - nodes of the graph to be created.
239-
* @param {Array.<Object>} data.links - links that connect data.nodes.
240-
* @returns {Object}
241-
*/
242-
_initializeGraphState(data) {
243-
let graph;
244-
245-
this._validateGraphData(data);
246-
247-
if (this.state && this.state.nodes && this.state.links && this.state.nodeIndexMapping) {
248-
// absorve existent positining
249-
graph = {
250-
nodes: data.nodes.map(n => Object.assign({}, n, this.state.nodes[n.id])),
251-
links: {}
252-
};
253-
} else {
254-
graph = {
255-
nodes: data.nodes.map(n => Object.assign({}, n)),
256-
links: {}
257-
};
258-
}
259-
260-
graph.links = data.links.map(l => Object.assign({}, l));
261-
262-
let config = Object.assign({}, utils.merge(DEFAULT_CONFIG, this.props.config || {}));
263-
let {nodes, nodeIndexMapping} = graphHelper.initializeNodes(graph.nodes);
264-
let links = graphHelper.initializeLinks(graph.links); // Matrix of graph connections
265-
const {nodes: d3Nodes, links: d3Links} = graph;
266-
const id = this.props.id.replace(/ /g, '_');
267-
const simulation = graphHelper.createForceSimulation(config.width, config.height);
268-
269-
return {
270-
id,
271-
config,
272-
nodeIndexMapping,
273-
links,
274-
d3Links,
275-
nodes,
276-
d3Nodes,
277-
highlightedNode: '',
278-
simulation,
279-
newGraphElements: false,
280-
configUpdated: false,
281-
transform: 1
282-
};
283-
}
284-
285219
/**
286220
* Sets d3 tick function and configures other d3 stuff such as forces and drag events.
287221
*/
@@ -310,7 +244,7 @@ export default class Graph extends React.Component {
310244
utils.throwErr(this.constructor.name, ERRORS.GRAPH_NO_ID_PROP);
311245
}
312246

313-
this.state = this._initializeGraphState(this.props.data);
247+
this.state = graphHelper.initializeGraphState(this.props, this.state);
314248
}
315249

316250
componentWillReceiveProps(nextProps) {
@@ -322,7 +256,7 @@ export default class Graph extends React.Component {
322256
}
323257

324258
const configUpdated = !utils.isDeepEqual(nextProps.config, this.state.config);
325-
const state = newGraphElements ? this._initializeGraphState(nextProps.data) : this.state;
259+
const state = newGraphElements ? graphHelper.initializeGraphState(nextProps, this.state) : this.state;
326260
const config = configUpdated ? utils.merge(DEFAULT_CONFIG, nextProps.config || {}) : this.state.config;
327261

328262
// In order to properly update graph data we need to pause eventual d3 ongoing animations

src/components/node/const.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import CONFIG from '../graph/config';
2+
import CONST from '../../const';
3+
4+
export default {
5+
ARC: {
6+
START_ANGLE: 0,
7+
END_ANGLE: 2 * Math.PI
8+
},
9+
DEFAULT_NODE_SIZE: CONFIG.node.size,
10+
NODE_LABEL_DX: '.90em',
11+
NODE_LABEL_DY: '.35em',
12+
...CONST
13+
};

0 commit comments

Comments
 (0)