Skip to content

Commit 447709c

Browse files
committed
fix(tree): ensures pan/zoom can bind to multiple Tree instances (#100)
* test(tree): adds regression test for unique className refs
1 parent ef5acba commit 447709c

File tree

2 files changed

+23
-7
lines changed

2 files changed

+23
-7
lines changed

src/Tree/index.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ type TreeState = {
2121
isInitialRenderForDataset: boolean;
2222
};
2323

24-
const rd3tSvgClassName = 'rd3t-svg';
25-
const rd3tGClassName = 'rd3t-g';
26-
2724
class Tree extends React.Component<TreeProps, TreeState> {
2825
static defaultProps: Partial<TreeProps> = {
2926
onNodeClick: undefined,
@@ -68,6 +65,9 @@ class Tree extends React.Component<TreeProps, TreeState> {
6865
isTransitioning: false,
6966
};
7067

68+
svgInstanceRef = `rd3t-svg-${uuidv4()}`;
69+
gInstanceRef = `rd3t-g-${uuidv4()}`;
70+
7171
static getDerivedStateFromProps(nextProps: TreeProps, prevState: TreeState) {
7272
let derivedState: Partial<TreeState> = null;
7373
// Clone new data & assign internal properties if `data` object reference changed.
@@ -138,8 +138,8 @@ class Tree extends React.Component<TreeProps, TreeState> {
138138
*/
139139
bindZoomListener(props: TreeProps) {
140140
const { zoomable, scaleExtent, translate, zoom, onUpdate } = props;
141-
const svg = select(`.${rd3tSvgClassName}`);
142-
const g = select(`.${rd3tGClassName}`);
141+
const svg = select(`.${this.svgInstanceRef}`);
142+
const g = select(`.${this.gInstanceRef}`);
143143
if (zoomable) {
144144
// Sets initial offset, so that first pan and zoom does not jump back to default [0,0] coords.
145145
// @ts-ignore
@@ -481,11 +481,15 @@ class Tree extends React.Component<TreeProps, TreeState> {
481481
return (
482482
<div className={`rd3t-tree-container ${zoomable ? 'rd3t-grabbable' : undefined}`}>
483483
<style>{globalCss}</style>
484-
<svg className={[rd3tSvgClassName, svgClassName].join(' ')} width="100%" height="100%">
484+
<svg
485+
className={`rd3t-svg ${this.svgInstanceRef} ${svgClassName}`}
486+
width="100%"
487+
height="100%"
488+
>
485489
<TransitionGroupWrapper
486490
enableLegacyTransitions={enableLegacyTransitions}
487491
component="g"
488-
className={rd3tGClassName}
492+
className={`rd3t-g ${this.gInstanceRef}`}
489493
transform={`translate(${translate.x},${translate.y}) scale(${scale})`}
490494
>
491495
{links.map((linkData, i) => {

src/Tree/tests/index.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ describe('<Tree />', () => {
4747
expect(renderedComponent.find(Link).length).toBe(linkCount);
4848
});
4949

50+
// Ensures D3's pan & zoom listeners can bind to multiple trees in a single view (https://github.com/bkrem/react-d3-tree/issues/100).
51+
it("assigns unique ref classNames to each Tree instance's `svg` and primary `g` tag", () => {
52+
const tree1 = shallow(<Tree data={mockData} />);
53+
const tree2 = shallow(<Tree data={mockData} />);
54+
expect(tree1.find('.rd3t-svg').prop('className')).not.toBe(
55+
tree2.find('.rd3t-svg').prop('className')
56+
);
57+
expect(tree1.find('.rd3t-g').prop('className')).not.toBe(
58+
tree2.find('.rd3t-g').prop('className')
59+
);
60+
});
61+
5062
it('reassigns internal props if `props.data` changes', () => {
5163
// `assignInternalProperties` recurses by depth: 1 level -> 1 call
5264
const mockDataDepth = 3;

0 commit comments

Comments
 (0)