question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Wrong/Strange position nodes with Material-UI

See original GitHub issue

Hi ! It’s me again ^^

Describe the bug

I’ve made my tree graph with staticGraph property to true and I’d a good shape of tree diagram (see the first image of Sreenshot section)

So, to build a modeler, i set the staticGraphWithDragAndDrop to true and i’ve got this graph with that (see the 2nd picture of Screenshot section)

But i use Material UI for UI interface of my react app, and I made this system : You can see graph in medium size (like the seconde image upside) and you can see in full screen. The full width mode pass with a Dialog Component of Material UI and in this mode : I’VE GOT THE GOOD SHAPE (like the 1st image upside) ! If i came back, i’ve the wrong shape …

I tried some CSS or JS stuff but, nothing change…

To Reproduce

I’ve got two class to do that :

  • The container which i build position of nodes in loadData()function after render() :
[...]

class ResultGraph extends Component<ResultGraphProps, ResultGraphState> {
  constructor(props) {
    super(props);
    this.state = {
      project: null,
      data: null,
      isLoading: true,
      editNodeDialogOpen: false,
      nodeTarget: null,
      anchorElement: null,
      openCreateNodeOption: false,
      openEditNodeOption: false,
      openEditTypeNodeOption: false,
      typeRelationNodes: DirectionQuestionNeighbour.CHILD
    };
  }

  async componentDidMount() {
    try {
      await this.loadData(this.props.projectId);
    } catch (error) {
      toast.error('Impossible de récupérer le graph correspondant. Problème de connexion au serveur.');
      log.info('Impossible to recover corresponding graph', error);
    }
  }

 [...]

  /**
   * Method to have the number of rank in function of children nodes
   * @param listOfLinks - List of links needed
   * @param nodeFocusId - Id of node focus at start
   */
  getRankNodeInFunctionOfLinks(listOfLinks, nodeFocusId: string) {
    let numberOfRank = 0;
    const startList = _.cloneDeep(listOfLinks);

    // We iterate on the list of links and if our node is a source,
    // we then delete the links where it is source and we look again for the target node if it has links too
    startList.map(link => {
      if (link.source === nodeFocusId) {
        _.remove(startList, { source: nodeFocusId });
        numberOfRank++;
        numberOfRank += this.getRankNodeInFunctionOfLinks(startList, link.target);
      }
    });
    return numberOfRank;
  }

  fillNodeToMapAndGraphArray(nodeGraphInSameLine, nodesByRank, value, graphNodesTemp) {
    const nodeGraphInSameLineOrdered = _.orderBy(nodeGraphInSameLine, ['positionInLine', 'asc']);
    nodesByRank.set(value, nodeGraphInSameLineOrdered);

    nodeGraphInSameLineOrdered.map(nodeToPush => graphNodesTemp.push(nodeToPush));

  }

  [...]

  render() {

    const { dossardRestClient, projectId, classes } = this.props;
    const { project, data, isLoading, nodeTarget, editNodeDialogOpen } = this.state;

    if (isLoading) {
      return <LoaderData text={'Chargement de l\'arbre correspondant.'}/>;
    }

    return (
      <div>
        <Container>
          {/* Project's Name */}
          <h1>{project.name}</h1>

          {/* Timeline of the project */}
          <StepsLine
            step={StepProjectEnum.RESULTS}
            className={classes.timeLine}
            chevronLeftLink={'/' + projectId + '/details/affinage'}
            chevronRightDisable
          />
        </Container>

        <Col xl={10} md={10} offset={{ xl: 1, md: 1 }} className={classes.diagramContainer}>
          <DiagramResult model={data}
                         dossardRestClient={dossardRestClient}
                         onClickNode={this.onClickNode.bind(this)}
                         onDoubleClickNode={this.onClickRenameNode.bind(this)}
                         onRightClickNode={this.onRightClickNode.bind(this)}
                         onClickGraph={this.onClickGraph.bind(this)}/>
          <NodeEditDialog node={nodeTarget}
                          open={editNodeDialogOpen}
                          onClose={this.closeNodeEditDialog.bind(this)}
                          onSubmit={this.updateNode.bind(this)}/>

          {this.buildGraphMenu(classes)}
        </Col>
      </div>
    );
  }

  async loadData(projectId) {
    const projectFound = await this.getProjectNeed(projectId);
    const graphData = await this.getGraphNeed(projectId);
    const graphNodes = new Array(0);
    const graphNodesTemp = new Array(0);
    const numberChildrenByIdNodes = new Map();
    const nodesByRank = new Map();
    const nodesToNotAppear = new Array(0);
    const sizeOfGraph = 1700;
    const marginBetweenNodes = 200;

    // Initialize all variables which will changing :
    //    actualRank : informed on which rank we are
    //    xNode : position x of node
    //    yNode : position y of node
    //    positionInLine : informed position of node in same line
    //    nodeGraphInSameLine : nodes array in same rank
    let actualRank = 0;
    let xNode = 0;
    let yNode = 0;
    let positionNodeInLine = 1;
    let nodeGraphInSameLine = new Array(0);


    // First, we search links with AND nodes without children or parents in Array
    const allAndNodes = graphData.nodes.filter(node => node.family === TypeQuestionsEnum.AND);
    const linksForAndWithNoChildrenOrNoParents = new Array(0);
    allAndNodes.map(andNode => {
      const andNodeWithTarget = graphData.links.filter(link => link.source === andNode.id);
      const andNodeWithSource = graphData.links.filter(link => link.target === andNode.id);
      if (andNodeWithTarget.length === 0 || andNodeWithSource.length === 0) {
        linksForAndWithNoChildrenOrNoParents.push(andNode);
      }
    });

    // With last array, we're gonna find node thanks to links
    graphData.nodes.map(node => linksForAndWithNoChildrenOrNoParents.find(nodeAnd => nodeAnd.id === node.id) !== undefined && nodesToNotAppear.push(node));

    // We're gonna search nodes associate with AND nodes which have no children or parents
    nodesToNotAppear.map(node => {
      const linksAffiliate = graphData.links.filter(link => link.target === node.id);
      linksAffiliate.map(link => {
        const getAlsoParent = graphData.links.filter(linkParent => linkParent.target === link.source);
        if (getAlsoParent.length === 0) {
          const nodeToNotAppear = graphData.nodes.find(nodeToFound => nodeToFound.id === node.id);
          nodesToNotAppear.push(nodeToNotAppear);
        }
      });
      _.remove(graphData.links, link => link.source === node.id || link.target === node.id);
    });


    // Get rank node by rank
    graphData.nodes.map(node => {

      // We filter links object to find if this node have links associate
      // If yes, we check if node it's not contains of nodeToNotAppear Array to add him
      const nodesWithLinks = graphData.links.filter(link => link.source === node.id || link.target === node.id);
      if (nodesWithLinks.length !== 0) {
        if (linksForAndWithNoChildrenOrNoParents.find(nodeAnd => nodeAnd.id === node.id) === undefined) {
          // Get the rank of nodes in function of his links (number children nodes)
          const numberOfLevelNode = this.getRankNodeInFunctionOfLinks(graphData.links, node.id);
          numberChildrenByIdNodes.set(node.id, numberOfLevelNode);
        }
      }
    });


    // Sort map in function of nodes rank asc
    // @ts-ignore
    const numberChildrenByIdNodesSortedByValues = new Map([...numberChildrenByIdNodes.entries()].sort((a, b) => a[1] - b[1]));
    const lastNode = Array.from(numberChildrenByIdNodesSortedByValues.entries()).pop();

    // Iterate map on all nodes vertically
    numberChildrenByIdNodesSortedByValues.forEach((value: number, key: string) => {

      // Get the node matching
      const nodeFound = graphData.nodes.find(nodeInArray => nodeInArray.id === key);


      // If we're not in the same last rank, we increment y position, adjust actualRank and reinitialize positionNodeInLine
      // And we add on map all node in last rank ordered by position by their rank
      // And we add on temp array nodes with all properties but x position set after
      if (value !== actualRank) {

        this.fillNodeToMapAndGraphArray(nodeGraphInSameLine, nodesByRank, value - 1, graphNodesTemp);

        yNode += 200;
        actualRank = value;
        positionNodeInLine = 1;
        nodeGraphInSameLine = [];
      }


      // Got all children links
      const linksChild = graphData.links.filter(link => link.source === key);

      let nodePosition = 1;
      if (linksChild.length !== 0) {
        const firstParent = graphNodesTemp.find(node => node.id === linksChild[0].target);
        if (firstParent !== undefined) {
          nodePosition = (firstParent.positionInLine * 10) + positionNodeInLine;
        }
      } else {
        nodePosition = positionNodeInLine;
      }


      // We add on array node with all properties except x position
      nodeGraphInSameLine.push({
        id: nodeFound.id,
        questionLabel: nodeFound.questionLabel,
        family: nodeFound.family,
        size: nodeFound.family === TypeQuestionsEnum.AND && 500,
        graphRank: value,
        positionInLine: nodePosition,
        y: yNode
      });

      // For the last node, as we not pass again in first block where we fill map and push in array.
      if (key === lastNode[0]) {
        this.fillNodeToMapAndGraphArray(nodeGraphInSameLine, nodesByRank, value, graphNodesTemp);
      }

      positionNodeInLine++;

    });

    const lastNodeWithAllProperties = graphNodesTemp.find(nodeInArray => nodeInArray.id === lastNode[0]);

    // We iterate on map which has rank in index and an array of Nodes in same rank.
    // And we iterate on this node array to set in horizontal vision x node position
    nodesByRank.forEach((nodesArray: NodeInterface[], rank: number) => {
      let xIndex = 1;
      nodesArray.map(nodeToPosition => {

        const linksChild = graphData.links.filter(link => link.source === nodeToPosition.id);
        const linksParent = graphData.links.filter(link => link.target === nodeToPosition.id);
        const conditionNoLinks = linksParent.length === 0 && linksChild.length === 0;

        xNode = (sizeOfGraph - marginBetweenNodes * nodesArray.length) / 2 + marginBetweenNodes * xIndex;
        graphNodes.push({
          ...nodeToPosition,
          positionInLine: nodeToPosition.positionInLine,
          x: xNode,
          y: conditionNoLinks ? lastNodeWithAllProperties.y + 400 : nodeToPosition.y          // In case of node have no links associate, we put him in bottom with 1 difference line
        });


        xIndex++;

      });

    });

    this.setState({
      project: projectFound,
      data: {
        nodes: graphNodes,
        links: graphData.links
      },
      isLoading: false
    });
  }

 [...]

export default withStyles(styles)(ResultGraph);
  • The component with Graph Object inside :
[...]
/**
 * Configuration for react-d3-graph
 */

const myConfig = {
  'automaticRearrangeAfterDropNode': false,
  'collapsible': false,
  'directed': true,
  'focusAnimationDuration': 0.75,
  'height': 700,
  'highlightDegree': 2,
  'highlightOpacity': 0.2,
  'linkHighlightBehavior': true,
  'maxZoom': 12,
  'minZoom': 0.05,
  'nodeHighlightBehavior': true,
  'panAndZoom': false,
  'staticGraph': false,
  'staticGraphWithDragAndDrop': true,
  'd3': {
    'alphaTarget': 0.05,
    'gravity': -100,
    'linkLength': 150,
    'linkStrength': 1
  },
  'node': {
    'mouseCursor': 'grab',
    'opacity': 1,
    'renderLabel': false,
    'size': 1500,
    'viewGenerator': node => <QuestionNodeSvg question={node}/>
  },
  'link': {
    'color': '#000',
    'highlightColor': 'red',
    'mouseCursor': 'pointer',
    'opacity': 1,
    'semanticStrokeWidth': false,
    'strokeWidth': 2
  }
};
[...]

class DiagramResult extends Component<DiagramResultProps, DiagramResultState> {
  constructor(props) {
    super(props);

    this.state = {
      config: myConfig,
      data: this.props.model,
      fullWidth: false,
      editNodeDialogOpen: false,
      nodeTarget: null,
      isLoading: true
    };
  }

 [...]
  render() {
    const { model, onClickNode, onDoubleClickNode, onRightClickNode, onClickGraph, classes } = this.props;
    const { fullWidth } = this.state;


    const graphProps = {
      id: 'treeDiagram',
      data: model,
      config: this.state.config,
      onClickNode,
      onDoubleClickNode,
      onRightClickNode,
      onClickGraph
    };

    if (fullWidth){
      graphProps.config = Object.assign({}, graphProps.config, {
        height: window.innerHeight - 10,
        width: window.innerWidth - 10
      });
    }

    const graph = <Graph {...graphProps} />

    if (fullWidth) {
      return (
        <Dialog fullScreen onClose={this.closeFullDiagram.bind(this)} open={fullWidth}
                className={classes.fullDiagramDialog}>
          <ActionButton icon={<Close/>} onClick={this.closeFullDiagram.bind(this)}
                        classes={{ iconButton: classes.closeFullDiagramButton }}/>
          {graph}
        </Dialog>
      );
    } else {
      return (
        <div className={classes.diagram}>
          {graph}
          <ActionButton
            icon={<ZoomOutMap/>}
            onClick={this.openFullDiagram.bind(this)}
            classes={{
              iconButton: classes.seeFullDiagramButton
            }}
          />
        </div>
      );
    }
  }
}


export default withStyles(styles)(DiagramResult);

Expected behavior

I want to have two graph with the same shape (like the first image)

Screenshots

First picture (good shape wanted and graph in full screen mode ) : TreeGraphGoodShpae

Second picture (bad shape not wanted in normal mode) : TreeGraphBadShape

Environment:

  • OS: Windows
  • Browser: Chrome
  • Version: latest
  • Node version: 10.16.3
  • react-d3-graph version : 2.3.0
  • d3 version: 5.12.0
  • react version : 16.10.1

Thanks in advance for your help

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
StevenHevencommented, Mar 2, 2020

hi @danielcaldas ,

I didn’t find solution, but i keep configuration in full screen and this one worked ! So, it doesn’t matter for the minimum configuration doesn’t work. Thanks anyway dude ! 😉

1reaction
StevenHevencommented, Dec 13, 2019

Oh okey, no problem dude, thx in advance to you or someone who take a look to fix this ! 😉

Read more comments on GitHub >

github_iconTop Results From Across the Web

Material UI - tooltip displays unexpectedly - Stack Overflow
Now the problem is that the second tooltip will not show as expected after changing from the first tooltip by clicking the button....
Read more >
[Popper] Popper with display:flex show wrong position #17859
Wrong position The popper has a style like:transform: translate3d(225px, 50px, 0px) But in the dev tools it is not 225,It will be another ......
Read more >
Troubleshooting - Material UI - MUI
This error comes from Fade , Grow , Slide , Zoom components due to a missing DOM node. Make sure that the children...
Read more >
Using material-table in React to build feature-rich data tables
Let's review how to create tables in React using one of the best and most feature-rich libraries available: material-table.
Read more >
Testing Material UI form components - DEV Community ‍ ‍
I used Material-UI for a side project and I ran into problems when trying ... <InputAdornment position="end"> <IconButton aria-label="submit ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found