summaryrefslogtreecommitdiffstats
path: root/openecomp-ui/src/nfvo-components/tree
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-ui/src/nfvo-components/tree')
-rw-r--r--openecomp-ui/src/nfvo-components/tree/Tree.jsx406
-rw-r--r--openecomp-ui/src/nfvo-components/tree/Tree.stories.js242
2 files changed, 372 insertions, 276 deletions
diff --git a/openecomp-ui/src/nfvo-components/tree/Tree.jsx b/openecomp-ui/src/nfvo-components/tree/Tree.jsx
index 682f3b6d50..39434fcdf1 100644
--- a/openecomp-ui/src/nfvo-components/tree/Tree.jsx
+++ b/openecomp-ui/src/nfvo-components/tree/Tree.jsx
@@ -1,16 +1,28 @@
-import React, {Component} from 'react';
+import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import {select} from 'd3-selection';
-import {tree, stratify} from 'd3-hierarchy';
-
+import { select } from 'd3-selection';
+import { tree, stratify } from 'd3-hierarchy';
function diagonal(d) {
-
- const offset = 50;
- return 'M' + d.y + ',' + d.x
- + 'C' + (d.parent.y + offset) + ',' + d.x
- + ' ' + (d.parent.y + offset) + ',' + d.parent.x
- + ' ' + d.parent.y + ',' + d.parent.x;
+ const offset = 50;
+ return (
+ 'M' +
+ d.y +
+ ',' +
+ d.x +
+ 'C' +
+ (d.parent.y + offset) +
+ ',' +
+ d.x +
+ ' ' +
+ (d.parent.y + offset) +
+ ',' +
+ d.parent.x +
+ ' ' +
+ d.parent.y +
+ ',' +
+ d.parent.x
+ );
}
const nodeRadius = 8;
@@ -18,164 +30,226 @@ const verticalSpaceBetweenNodes = 70;
const NARROW_HORIZONTAL_SPACES = 47;
const WIDE_HORIZONTAL_SPACES = 65;
-const stratifyFn = stratify().id(d => d.id).parentId(d => d.parent);
+const stratifyFn = stratify()
+ .id(d => d.id)
+ .parentId(d => d.parent);
class Tree extends Component {
-
- // state = {
- // startingCoordinates: null,
- // isDown: false
- // }
-
- static propTypes = {
- name: PropTypes.string,
- width: PropTypes.number,
- allowScaleWidth: PropTypes.bool,
- nodes: PropTypes.arrayOf(PropTypes.shape({
- id: PropTypes.string,
- name: PropTypes.string,
- parent: PropTypes.string
- })),
- selectedNodeId: PropTypes.string,
- onNodeClick: PropTypes.func,
- onRenderedBeyondWidth: PropTypes.func
- };
-
- static defaultProps = {
- width: 500,
- allowScaleWidth : true,
- name: 'default-name'
- };
-
- render() {
- let {width, name, scrollable = false} = this.props;
- return (
- <div
- className={`tree-view ${name}-container ${scrollable ? 'scrollable' : ''}`}>
- <svg width={width} className={name}></svg>
- </div>
- );
- }
-
- componentDidMount() {
- this.renderTree();
- }
-
- // handleMouseMove(e) {
- // if (!this.state.isDown) {
- // return;
- // }
- // const container = select(`.tree-view.${this.props.name}-container`);
- // let coordinates = this.getCoordinates(e);
- // container.property('scrollLeft' , container.property('scrollLeft') + coordinates.x - this.state.startingCoordinates.x);
- // container.property('scrollTop' , container.property('scrollTop') + coordinates.y - this.state.startingCoordinates.y);
- // }
-
- // handleMouseDown(e) {
- // let startingCoordinates = this.getCoordinates(e);
- // this.setState({
- // startingCoordinates,
- // isDown: true
- // });
- // }
-
- // handleMouseUp() {
- // this.setState({
- // startingCorrdinates: null,
- // isDown: false
- // });
- // }
-
- // getCoordinates(e) {
- // var bounds = e.target.getBoundingClientRect();
- // var x = e.clientX - bounds.left;
- // var y = e.clientY - bounds.top;
- // return {x, y};
- // }
-
- componentDidUpdate(prevProps) {
- if (this.props.nodes.length !== prevProps.nodes.length ||
- this.props.selectedNodeId !== prevProps.selectedNodeId) {
- console.log('update');
- this.renderTree();
- }
- }
-
- renderTree() {
- let {width, nodes, name, allowScaleWidth, selectedNodeId, onRenderedBeyondWidth, toWiden} = this.props;
- if (nodes.length > 0) {
-
- let horizontalSpaceBetweenLeaves = toWiden ? WIDE_HORIZONTAL_SPACES : NARROW_HORIZONTAL_SPACES;
- const treeFn = tree().nodeSize([horizontalSpaceBetweenLeaves, verticalSpaceBetweenNodes]);//.size([width - 50, height - 50])
- let root = stratifyFn(nodes).sort((a, b) => a.data.name.localeCompare(b.data.name));
- let svgHeight = verticalSpaceBetweenNodes * root.height + nodeRadius * 6;
-
- treeFn(root);
-
- let nodesXValue = root.descendants().map(node => node.x);
- let maxX = Math.max(...nodesXValue);
- let minX = Math.min(...nodesXValue);
-
- let svgTempWidth = (maxX - minX) / 30 * (horizontalSpaceBetweenLeaves);
- let svgWidth = svgTempWidth < width ? (width - 5) : svgTempWidth;
- const svgEL = select(`svg.${name}`);
- const container = select(`.tree-view.${name}-container`);
- svgEL.html('');
- svgEL.attr('height', svgHeight);
- let canvasWidth = width;
- if (svgTempWidth > width) {
- if (allowScaleWidth) {
- canvasWidth = svgTempWidth;
- }
- // we seems to have a margin of 25px that we can still see with text
- if (((svgTempWidth - 25) > width) && onRenderedBeyondWidth !== undefined) {
- onRenderedBeyondWidth();
- }
- };
- svgEL.attr('width', canvasWidth);
- let rootGroup = svgEL.append('g').attr('transform', `translate(${svgWidth / 2 + nodeRadius},${nodeRadius * 4}) rotate(90)`);
-
- // handle link
- rootGroup.selectAll('.link')
- .data(root.descendants().slice(1))
- .enter().append('path')
- .attr('class', 'link')
- .attr('d', diagonal);
-
- let node = rootGroup.selectAll('.node')
- .data(root.descendants())
- .enter().append('g')
- .attr('class', node => `node ${node.children ? ' has-children' : ' leaf'} ${node.id === selectedNodeId ? 'selectedNode' : ''} ${this.props.onNodeClick ? 'clickable' : ''}`)
- .attr('transform', node => 'translate(' + node.y + ',' + node.x + ')')
- .on('click', node => this.onNodeClick(node));
-
- node.append('circle').attr('r', nodeRadius).attr('class', 'outer-circle');
- node.append('circle').attr('r', nodeRadius - 3).attr('class', 'inner-circle');
-
- node.append('text')
- .attr('y', nodeRadius / 4 + 1)
- .attr('x', - nodeRadius * 1.8)
- .text(node => node.data.name)
- .attr('transform', 'rotate(-90)');
-
- let selectedNode = selectedNodeId ? root.descendants().find(node => node.id === selectedNodeId) : null;
- if (selectedNode) {
-
- container.property('scrollLeft', (svgWidth / 4) + (svgWidth / 4 - 100) - (selectedNode.x / 30 * horizontalSpaceBetweenLeaves));
- container.property('scrollTop', (selectedNode.y / 100 * verticalSpaceBetweenNodes));
-
- } else {
- container.property('scrollLeft', (svgWidth / 4) + (svgWidth / 4 - 100));
- }
- }
- }
-
- onNodeClick(node) {
- if (this.props.onNodeClick) {
- this.props.onNodeClick(node.data);
- }
- }
-
+ // state = {
+ // startingCoordinates: null,
+ // isDown: false
+ // }
+
+ static propTypes = {
+ name: PropTypes.string,
+ width: PropTypes.number,
+ allowScaleWidth: PropTypes.bool,
+ nodes: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string,
+ name: PropTypes.string,
+ parent: PropTypes.string
+ })
+ ),
+ selectedNodeId: PropTypes.string,
+ onNodeClick: PropTypes.func,
+ onRenderedBeyondWidth: PropTypes.func
+ };
+
+ static defaultProps = {
+ width: 500,
+ allowScaleWidth: true,
+ name: 'default-name'
+ };
+
+ render() {
+ let { width, name, scrollable = false } = this.props;
+ return (
+ <div
+ className={`tree-view ${name}-container ${
+ scrollable ? 'scrollable' : ''
+ }`}>
+ <svg width={width} className={name} />
+ </div>
+ );
+ }
+
+ componentDidMount() {
+ this.renderTree();
+ }
+
+ // handleMouseMove(e) {
+ // if (!this.state.isDown) {
+ // return;
+ // }
+ // const container = select(`.tree-view.${this.props.name}-container`);
+ // let coordinates = this.getCoordinates(e);
+ // container.property('scrollLeft' , container.property('scrollLeft') + coordinates.x - this.state.startingCoordinates.x);
+ // container.property('scrollTop' , container.property('scrollTop') + coordinates.y - this.state.startingCoordinates.y);
+ // }
+
+ // handleMouseDown(e) {
+ // let startingCoordinates = this.getCoordinates(e);
+ // this.setState({
+ // startingCoordinates,
+ // isDown: true
+ // });
+ // }
+
+ // handleMouseUp() {
+ // this.setState({
+ // startingCorrdinates: null,
+ // isDown: false
+ // });
+ // }
+
+ // getCoordinates(e) {
+ // var bounds = e.target.getBoundingClientRect();
+ // var x = e.clientX - bounds.left;
+ // var y = e.clientY - bounds.top;
+ // return {x, y};
+ // }
+
+ componentDidUpdate(prevProps) {
+ if (
+ this.props.nodes.length !== prevProps.nodes.length ||
+ this.props.selectedNodeId !== prevProps.selectedNodeId
+ ) {
+ console.log('update');
+ this.renderTree();
+ }
+ }
+
+ renderTree() {
+ let {
+ width,
+ nodes,
+ name,
+ allowScaleWidth,
+ selectedNodeId,
+ onRenderedBeyondWidth,
+ toWiden
+ } = this.props;
+ if (nodes.length > 0) {
+ let horizontalSpaceBetweenLeaves = toWiden
+ ? WIDE_HORIZONTAL_SPACES
+ : NARROW_HORIZONTAL_SPACES;
+ const treeFn = tree().nodeSize([
+ horizontalSpaceBetweenLeaves,
+ verticalSpaceBetweenNodes
+ ]); //.size([width - 50, height - 50])
+ let root = stratifyFn(nodes).sort((a, b) =>
+ a.data.name.localeCompare(b.data.name)
+ );
+ let svgHeight =
+ verticalSpaceBetweenNodes * root.height + nodeRadius * 6;
+
+ treeFn(root);
+
+ let nodesXValue = root.descendants().map(node => node.x);
+ let maxX = Math.max(...nodesXValue);
+ let minX = Math.min(...nodesXValue);
+
+ let svgTempWidth =
+ (maxX - minX) / 30 * horizontalSpaceBetweenLeaves;
+ let svgWidth = svgTempWidth < width ? width - 5 : svgTempWidth;
+ const svgEL = select(`svg.${name}`);
+ const container = select(`.tree-view.${name}-container`);
+ svgEL.html('');
+ svgEL.attr('height', svgHeight);
+ let canvasWidth = width;
+ if (svgTempWidth > width) {
+ if (allowScaleWidth) {
+ canvasWidth = svgTempWidth;
+ }
+ // we seems to have a margin of 25px that we can still see with text
+ if (
+ svgTempWidth - 25 > width &&
+ onRenderedBeyondWidth !== undefined
+ ) {
+ onRenderedBeyondWidth();
+ }
+ }
+ svgEL.attr('width', canvasWidth);
+ let rootGroup = svgEL
+ .append('g')
+ .attr(
+ 'transform',
+ `translate(${svgWidth / 2 + nodeRadius},${nodeRadius *
+ 4}) rotate(90)`
+ );
+
+ // handle link
+ rootGroup
+ .selectAll('.link')
+ .data(root.descendants().slice(1))
+ .enter()
+ .append('path')
+ .attr('class', 'link')
+ .attr('d', diagonal);
+
+ let node = rootGroup
+ .selectAll('.node')
+ .data(root.descendants())
+ .enter()
+ .append('g')
+ .attr(
+ 'class',
+ node =>
+ `node ${node.children ? ' has-children' : ' leaf'} ${
+ node.id === selectedNodeId ? 'selectedNode' : ''
+ } ${this.props.onNodeClick ? 'clickable' : ''}`
+ )
+ .attr(
+ 'transform',
+ node => 'translate(' + node.y + ',' + node.x + ')'
+ )
+ .on('click', node => this.onNodeClick(node));
+
+ node
+ .append('circle')
+ .attr('r', nodeRadius)
+ .attr('class', 'outer-circle');
+ node
+ .append('circle')
+ .attr('r', nodeRadius - 3)
+ .attr('class', 'inner-circle');
+
+ node
+ .append('text')
+ .attr('y', nodeRadius / 4 + 1)
+ .attr('x', -nodeRadius * 1.8)
+ .text(node => node.data.name)
+ .attr('transform', 'rotate(-90)');
+
+ let selectedNode = selectedNodeId
+ ? root.descendants().find(node => node.id === selectedNodeId)
+ : null;
+ if (selectedNode) {
+ container.property(
+ 'scrollLeft',
+ svgWidth / 4 +
+ (svgWidth / 4 - 100) -
+ selectedNode.x / 30 * horizontalSpaceBetweenLeaves
+ );
+ container.property(
+ 'scrollTop',
+ selectedNode.y / 100 * verticalSpaceBetweenNodes
+ );
+ } else {
+ container.property(
+ 'scrollLeft',
+ svgWidth / 4 + (svgWidth / 4 - 100)
+ );
+ }
+ }
+ }
+
+ onNodeClick(node) {
+ if (this.props.onNodeClick) {
+ this.props.onNodeClick(node.data);
+ }
+ }
}
export default Tree;
diff --git a/openecomp-ui/src/nfvo-components/tree/Tree.stories.js b/openecomp-ui/src/nfvo-components/tree/Tree.stories.js
index b29920b3ec..aca1a13402 100644
--- a/openecomp-ui/src/nfvo-components/tree/Tree.stories.js
+++ b/openecomp-ui/src/nfvo-components/tree/Tree.stories.js
@@ -1,119 +1,141 @@
import React from 'react';
-import {storiesOf} from '@kadira/storybook';
-import {withKnobs} from '@kadira/storybook-addon-knobs';
+import { storiesOf } from '@kadira/storybook';
+import { withKnobs } from '@kadira/storybook-addon-knobs';
import Tree from './Tree.jsx';
-import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js';
const stories = storiesOf('Version Tree', module);
stories.addDecorator(withKnobs);
const response = {
- listCount: 6,
- results: [
- {
- 'id': '123',
- 'name': '1.0',
- 'description': 'string',
- 'baseId': '',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- },
- {
- 'id': '1234',
- 'name': '1.1',
- 'description': 'string',
- 'baseId': '123',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- },
- {
- 'id': '12345',
- 'name': '2.0',
- 'description': 'string',
- 'baseId': '123',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- },
- {
- 'id': '123456',
- 'name': '3.0',
- 'description': 'string',
- 'baseId': '12345',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- },
- {
- 'id': '1234567',
- 'name': '1.2',
- 'description': 'string',
- 'baseId': '1234',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- },
- {
- 'id': '12345678',
- 'name': '2.1',
- 'description': 'string',
- 'baseId': '12345',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- },
- {
- 'id': '123456789',
- 'name': '4.0',
- 'description': 'string',
- 'baseId': '123456',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- },
- {
- 'id': '12345678910',
- 'name': '3.1',
- 'description': 'string',
- 'baseId': '123456',
- 'status': 'Draft',
- 'creationTime': '2017-06-08T08:55:37.831Z',
- 'modificationTime': '2017-06-08T08:55:37.831Z'
- }
- ]
+ listCount: 6,
+ results: [
+ {
+ id: '123',
+ name: '1.0',
+ description: 'string',
+ baseId: '',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ },
+ {
+ id: '1234',
+ name: '1.1',
+ description: 'string',
+ baseId: '123',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ },
+ {
+ id: '12345',
+ name: '2.0',
+ description: 'string',
+ baseId: '123',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ },
+ {
+ id: '123456',
+ name: '3.0',
+ description: 'string',
+ baseId: '12345',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ },
+ {
+ id: '1234567',
+ name: '1.2',
+ description: 'string',
+ baseId: '1234',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ },
+ {
+ id: '12345678',
+ name: '2.1',
+ description: 'string',
+ baseId: '12345',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ },
+ {
+ id: '123456789',
+ name: '4.0',
+ description: 'string',
+ baseId: '123456',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ },
+ {
+ id: '12345678910',
+ name: '3.1',
+ description: 'string',
+ baseId: '123456',
+ status: 'Draft',
+ creationTime: '2017-06-08T08:55:37.831Z',
+ modificationTime: '2017-06-08T08:55:37.831Z'
+ }
+ ]
};
-const divStyle = { width: '200px', borderStyle: 'solid', borderColor: 'black', border: '1px solid black'};
-const tree = response.results.map(item => ({id: item.id, name: item.name, parent: item.baseId}));
-const nodeClickHandler = function (node) {
- window.alert(node.name);
+const divStyle = {
+ width: '200px',
+ borderStyle: 'solid',
+ borderColor: 'black',
+ border: '1px solid black'
};
-stories.add('Classic Version Tree', () => (
- <div>
- <Tree nodes={tree} onNodeClick={nodeClickHandler} selectedNodeId={'1234'}/>
- </div>
-)).add('Single Version Tree', () => (
- <div>
- <Tree nodes={[tree[0]]} onNodeClick={nodeClickHandler}/>
- </div>
-)).add('Single Path Version Tree', () => (
- <div>
- <Tree nodes={[tree[0], tree[1]]} onNodeClick={nodeClickHandler}/>
- </div>
-)).add('Empty Tree', () => (
- <div>
- <Tree nodes={[]}/>
- </div>
-)).add('Add Tree in Version Page Frame', () => (
- <div style={divStyle}>
- Tree wider than frame<br/><br/><br/>
- <Tree
- name={'versions-tree'}
- width={200}
- nodes={tree}
- onRenderedBeyondWidth={() => {console.log('rendered beyond width')}}
- allowScaleWidth={false}
- onNodeClick={nodeClickHandler}/>
- </div>
-));
+const tree = response.results.map(item => ({
+ id: item.id,
+ name: item.name,
+ parent: item.baseId
+}));
+const nodeClickHandler = function(node) {
+ window.alert(node.name);
+};
+stories
+ .add('Classic Version Tree', () => (
+ <div>
+ <Tree
+ nodes={tree}
+ onNodeClick={nodeClickHandler}
+ selectedNodeId={'1234'}
+ />
+ </div>
+ ))
+ .add('Single Version Tree', () => (
+ <div>
+ <Tree nodes={[tree[0]]} onNodeClick={nodeClickHandler} />
+ </div>
+ ))
+ .add('Single Path Version Tree', () => (
+ <div>
+ <Tree nodes={[tree[0], tree[1]]} onNodeClick={nodeClickHandler} />
+ </div>
+ ))
+ .add('Empty Tree', () => (
+ <div>
+ <Tree nodes={[]} />
+ </div>
+ ))
+ .add('Add Tree in Version Page Frame', () => (
+ <div style={divStyle}>
+ Tree wider than frame<br />
+ <br />
+ <br />
+ <Tree
+ name={'versions-tree'}
+ width={200}
+ nodes={tree}
+ onRenderedBeyondWidth={() => {
+ console.log('rendered beyond width');
+ }}
+ allowScaleWidth={false}
+ onNodeClick={nodeClickHandler}
+ />
+ </div>
+ ));