Skip to content

Commit 460e176

Browse files
committed
Implements textLayout prop; closes #34
Fixes missing fill for node names & attrs Implements textLayout, adds unit tests Adds docs for `textLayout`
1 parent b99192d commit 460e176

File tree

5 files changed

+66
-28
lines changed

5 files changed

+66
-28
lines changed

README.md

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -79,24 +79,25 @@ class MyComponent extends React.Component {
7979

8080

8181
## Props
82-
| Property | Type | Options | Required? | Default | Description |
83-
|:------------------------|:----------------|:--------------------------------------|:----------|:---------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
84-
| `data` | `array` | | required | `undefined` | Single-element array containing hierarchical object (see `myTreeData` above). <br /> Contains (at least) `name` and `parent` keys. |
85-
| `nodeSvgShape` | `object` | see [Node shapes](#node-shapes) | | `{shape: 'circle', shapeProps: r: 10}` | Sets a specific SVG shape element + shapeProps to be used for each node. |
86-
| `onClick` | `func` | | | `undefined` | Callback function to be called whenever a node is clicked. <br /><br /> The clicked node's data object is passed to the callback function as the first parameter. |
87-
| `orientation` | `string` (enum) | `horizontal` `vertical` | | `horizontal` | `horizontal` - Tree expands left-to-right. <br /><br /> `vertical` - Tree expands top-to-bottom. |
88-
| `translate` | `object` | | | `{x: 0, y: 0}` | Translates the graph along the x/y axis by the specified amount of pixels (avoids the graph being stuck in the top left canvas corner). |
89-
| `pathFunc` | `string` (enum) | `diagonal` `elbow` `straight` | | `diagonal` | `diagonal` - Renders smooth, curved edges between parent-child nodes. <br /><br /> `elbow` - Renders sharp edges at right angles between parent-child nodes. <br /><br /> `straight` - Renders straight lines between parent-child nodes. |
90-
| `collapsible` | `bool` | | | `true` | Toggles ability to collapse/expand the tree's nodes by clicking them. |
91-
| `initialDepth` | `number` | `0..n` | | `undefined` | Sets the maximum node depth to which the tree is expanded on its initial render. <br /> Tree renders to full depth if prop is omitted. |
92-
| `depthFactor` | `number` | `-n..0..n` | | `undefined` | Ensures the tree takes up a fixed amount of space (`node.y = node.depth * depthFactor`), regardless of tree depth. <br /> **TIP**: Negative values invert the tree's direction. |
93-
| `zoomable` | `bool` | | | `true` | Toggles ability to zoom in/out on the Tree by scaling it according to `props.scaleExtent`. |
94-
| `scaleExtent` | `object` | `{min: 0..n, max: 0..n}` | | `{min: 0.1, max: 1}` | Sets the minimum/maximum extent to which the tree can be scaled if `props.zoomable` is true. |
95-
| `nodeSize` | `object` | `{x: 0..n, y: 0..n}` | | `{x: 140, y: 140}` | Sets a fixed size for each node. <br /><br /> This does not affect node circle sizes, circle sizes are handled by the `circleRadius` prop. |
96-
| `separation` | `object` | `{siblings: 0..n, nonSiblings: 0..n}` | | `{siblings: 1, nonSiblings: 2}` | Sets separation between neighbouring nodes, differentiating between siblings (same parent) and non-siblings. |
97-
| `transitionDuration` | `number` | `0..n` | | `500` | Sets the animation duration (in ms) of each expansion/collapse of a tree node. <br /><br /> Set this to `0` to deactivate animations completely. |
98-
| `styles` | `object` | see [Styling](#styling) | | `Node`/`Link` CSS files | Overrides and/or enhances the tree's default styling. |
99-
| `circleRadius` (legacy) | `number` | `0..n` | | `undefined` | Sets the radius of each node's `<circle>` element.<br /><br /> **Will be deprecated in v2, please use `nodeSvgShape` instead.** |
82+
| Property | Type | Options | Required? | Default | Description |
83+
|:------------------------|:----------------|:-----------------------------------------------|:----------|:----------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
84+
| `data` | `array` | | required | `undefined` | Single-element array containing hierarchical object (see `myTreeData` above). <br /> Contains (at least) `name` and `parent` keys. |
85+
| `nodeSvgShape` | `object` | see [Node shapes](#node-shapes) | | `{shape: 'circle', shapeProps: r: 10}` | Sets a specific SVG shape element + shapeProps to be used for each node. |
86+
| `onClick` | `func` | | | `undefined` | Callback function to be called whenever a node is clicked. <br /><br /> The clicked node's data object is passed to the callback function as the first parameter. |
87+
| `orientation` | `string` (enum) | `horizontal` `vertical` | | `horizontal` | `horizontal` - Tree expands left-to-right. <br /><br /> `vertical` - Tree expands top-to-bottom. |
88+
| `translate` | `object` | | | `{x: 0, y: 0}` | Translates the graph along the x/y axis by the specified amount of pixels (avoids the graph being stuck in the top left canvas corner). |
89+
| `pathFunc` | `string` (enum) | `diagonal` `elbow` `straight` | | `diagonal` | `diagonal` - Renders smooth, curved edges between parent-child nodes. <br /><br /> `elbow` - Renders sharp edges at right angles between parent-child nodes. <br /><br /> `straight` - Renders straight lines between parent-child nodes. |
90+
| `collapsible` | `bool` | | | `true` | Toggles ability to collapse/expand the tree's nodes by clicking them. |
91+
| `initialDepth` | `number` | `0..n` | | `undefined` | Sets the maximum node depth to which the tree is expanded on its initial render. <br /> Tree renders to full depth if prop is omitted. |
92+
| `depthFactor` | `number` | `-n..0..n` | | `undefined` | Ensures the tree takes up a fixed amount of space (`node.y = node.depth * depthFactor`), regardless of tree depth. <br /> **TIP**: Negative values invert the tree's direction. |
93+
| `zoomable` | `bool` | | | `true` | Toggles ability to zoom in/out on the Tree by scaling it according to `props.scaleExtent`. |
94+
| `scaleExtent` | `object` | `{min: 0..n, max: 0..n}` | | `{min: 0.1, max: 1}` | Sets the minimum/maximum extent to which the tree can be scaled if `props.zoomable` is true. |
95+
| `nodeSize` | `object` | `{x: 0..n, y: 0..n}` | | `{x: 140, y: 140}` | Sets a fixed size for each node. <br /><br /> This does not affect node circle sizes, circle sizes are handled by the `circleRadius` prop. |
96+
| `separation` | `object` | `{siblings: 0..n, nonSiblings: 0..n}` | | `{siblings: 1, nonSiblings: 2}` | Sets separation between neighbouring nodes, differentiating between siblings (same parent) and non-siblings. |
97+
| `transitionDuration` | `number` | `0..n` | | `500` | Sets the animation duration (in ms) of each expansion/collapse of a tree node. <br /><br /> Set this to `0` to deactivate animations completely. |
98+
| `textLayout` | `object` | `{textAnchor: enum, x: -n..0..n, y: -n..0..n}` | | `{textAnchor: "start", x: 10, y: -10 }` | Configures the positioning of each node's text (name & attributes) relative to the node itself.<br/><br/>`textAnchor` enums mirror the [`text-anchor` spec](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor).<br/>`x` & `y` accept integers denoting `px` values. |
99+
| `styles` | `object` | see [Styling](#styling) | | `Node`/`Link` CSS files | Overrides and/or enhances the tree's default styling. |
100+
| `circleRadius` (legacy) | `number` | `0..n` | | `undefined` | Sets the radius of each node's `<circle>` element.<br /><br /> **Will be deprecated in v2, please use `nodeSvgShape` instead.** |
100101

101102

102103
## Node shapes

src/Node/index.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export default class Node extends React.Component {
6868
}
6969

7070
render() {
71-
const { nodeData, nodeSvgShape, styles } = this.props;
71+
const { nodeData, nodeSvgShape, textLayout, styles } = this.props;
7272
const nodeStyle = nodeData._children
7373
? { ...styles.node }
7474
: { ...styles.leafNode };
@@ -95,23 +95,23 @@ export default class Node extends React.Component {
9595

9696
<text
9797
className="nodeNameBase"
98-
textAnchor={this.props.textAnchor}
9998
style={nodeStyle.name}
100-
x="10"
101-
y="-10"
99+
textAnchor={textLayout.textAnchor}
100+
x={textLayout.x}
101+
y={textLayout.y}
102102
dy=".35em"
103103
>
104104
{this.props.name}
105105
</text>
106106
<text
107107
className="nodeAttributesBase"
108-
y="0"
109-
textAnchor={this.props.textAnchor}
108+
y={textLayout.y + 10}
109+
textAnchor={textLayout.textAnchor}
110110
style={nodeStyle.attributes}
111111
>
112112
{this.props.attributes &&
113113
Object.keys(this.props.attributes).map(labelKey => (
114-
<tspan x="10" dy="1.2em" key={uuid.v4()}>
114+
<tspan x={textLayout.x} dy="1.2em" key={uuid.v4()}>
115115
{labelKey}: {this.props.attributes[labelKey]}
116116
</tspan>
117117
))}
@@ -147,7 +147,7 @@ Node.propTypes = {
147147
onClick: PropTypes.func.isRequired,
148148
name: PropTypes.string.isRequired,
149149
attributes: PropTypes.object,
150-
textAnchor: PropTypes.string,
150+
textLayout: PropTypes.object.isRequired,
151151
circleRadius: PropTypes.number,
152152
styles: PropTypes.object,
153153
};

src/Node/style.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
}
1414

1515
.nodeNameBase {
16+
fill: #000;
1617
stroke: #000;
1718
stroke-width: 1;
1819
}
1920

2021
.nodeAttributesBase {
22+
fill: #777;
2123
stroke: #777;
2224
stroke-width: 1;
2325
font-size: smaller;

src/Node/tests/index.test.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,18 @@ describe('<Node />', () => {
2525
},
2626
},
2727
name: nodeData.name,
28+
attributes: {
29+
testkeyA: 'testvalA',
30+
testKeyB: 'testvalB',
31+
},
2832
orientation: 'horizontal',
2933
transitionDuration: 500,
3034
onClick: () => {},
35+
textLayout: {
36+
textAnchor: 'start',
37+
x: 10,
38+
y: -10,
39+
},
3140
styles: {},
3241
};
3342

@@ -182,6 +191,24 @@ describe('<Node />', () => {
182191
).toBe(1);
183192
});
184193

194+
it('applies the `textLayout` prop to the node name & attributes', () => {
195+
const fixture = {
196+
textAnchor: 'test',
197+
x: 999,
198+
y: 111,
199+
};
200+
const renderedComponent = shallow(
201+
<Node {...mockProps} textLayout={fixture} />,
202+
);
203+
const nodeName = renderedComponent
204+
.find('text')
205+
.findWhere(n => n.prop('className') === 'nodeNameBase');
206+
const nodeAttribute = renderedComponent.find('tspan').first();
207+
208+
expect(nodeName.props()).toEqual(expect.objectContaining(fixture));
209+
expect(nodeAttribute.prop('x')).toBe(fixture.x);
210+
});
211+
185212
it('applies its own x/y coords on `transform` once mounted', () => {
186213
const fixture = `translate(${nodeData.y},${nodeData.x})`;
187214
const renderedComponent = mount(<Node {...mockProps} />);
@@ -219,7 +246,7 @@ describe('<Node />', () => {
219246
});
220247

221248
it('allows passing SVG shape elements + shapeProps to be used as the node element', () => {
222-
const fixture = { shape: 'ellipsis', shapeProps: { rx: 20, ry: 10 } };
249+
const fixture = { shape: 'ellipse', shapeProps: { rx: 20, ry: 10 } };
223250
const props = { ...mockProps, nodeSvgShape: fixture };
224251
const renderedComponent = shallow(<Node {...props} />);
225252

@@ -234,6 +261,7 @@ describe('<Node />', () => {
234261
const props = { ...mockProps, circleRadius: 99 };
235262
const renderedComponent = shallow(<Node {...props} />);
236263

264+
expect(renderedComponent.find('circle').length).toBe(1);
237265
expect(renderedComponent.find('circle').prop('r')).toBe(99);
238266
});
239267

src/Tree/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ export default class Tree extends React.Component {
267267
pathFunc,
268268
transitionDuration,
269269
zoomable,
270+
textLayout,
270271
circleRadius,
271272
styles,
272273
} = this.props;
@@ -300,11 +301,11 @@ export default class Tree extends React.Component {
300301
nodeSvgShape={nodeSvgShape}
301302
orientation={orientation}
302303
transitionDuration={transitionDuration}
303-
textAnchor="start"
304304
nodeData={nodeData}
305305
name={nodeData.name}
306306
attributes={nodeData.attributes}
307307
onClick={this.handleNodeToggle}
308+
textLayout={textLayout}
308309
circleRadius={circleRadius}
309310
styles={styles.nodes}
310311
/>
@@ -335,6 +336,11 @@ Tree.defaultProps = {
335336
scaleExtent: { min: 0.1, max: 1 },
336337
nodeSize: { x: 140, y: 140 },
337338
separation: { siblings: 1, nonSiblings: 2 },
339+
textLayout: {
340+
textAnchor: 'start',
341+
x: 10,
342+
y: -10,
343+
},
338344
circleRadius: undefined, // TODO: DEPRECATE
339345
styles: {},
340346
};
@@ -369,6 +375,7 @@ Tree.propTypes = {
369375
siblings: PropTypes.number,
370376
nonSiblings: PropTypes.number,
371377
}),
378+
textLayout: PropTypes.object,
372379
circleRadius: PropTypes.number,
373380
styles: PropTypes.shape({
374381
nodes: PropTypes.object,

0 commit comments

Comments
 (0)