All files / app/assets/javascripts/pipelines/components/graph_shared drawing_utils.js

96.67% Statements 29/30
66.67% Branches 6/9
100% Functions 3/3
96.55% Lines 28/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107    32x                         9x 15x   15x 34x   34x 34x   34x 34x   34x 34x   34x 32x 32x                   32x     32x       32x   32x       32x   32x         32x           32x     32x             32x 32x   32x             32x                 32x                  
import * as d3 from 'd3';
 
export const createUniqueLinkId = (stageName, jobName) => `${stageName}-${jobName}`;
 
/**
 * This function expects its first argument data structure
 * to be the same shaped as the one generated by `parseData`,
 * which contains nodes and links. For each link,
 * we find the nodes in the graph, calculate their coordinates and
 * trace the lines that represent the needs of each job.
 * @param {Object} nodeDict - Resulting object of `parseData` with nodes and links
 * @param {String} containerID - Id for the svg the links will be draw in
 * @returns {Array} Links that contain all the information about them
 */
 
export const generateLinksData = (links, containerID, modifier = '') => {
  const containerEl = document.getElementById(containerID);
 
  return links.map((link) => {
    const path = d3.path();
 
    const sourceId = link.source;
    const targetId = link.target;
 
    const modifiedSourceId = `${sourceId}${modifier}`;
    const modifiedTargetId = `${targetId}${modifier}`;
 
    const sourceNodeEl = document.getElementById(modifiedSourceId);
    const targetNodeEl = document.getElementById(modifiedTargetId);
 
    const sourceNodeCoordinates = sourceNodeEl.getBoundingClientRect();
    const targetNodeCoordinates = targetNodeEl.getBoundingClientRect();
    const containerCoordinates = containerEl.getBoundingClientRect();
 
    // Because we add the svg dynamically and calculate the coordinates
    // with plain JS and not D3, we need to account for the fact that
    // the coordinates we are getting are absolutes, but we want to draw
    // relative to the svg container, which starts at `containerCoordinates(x,y)`
    // so we substract these from the total. We also need to remove the padding
    // from the total to make sure it's aligned properly. We then make the line
    // positioned in the center of the job node by adding half the height
    // of the job pill.
    const paddingLeft = parseFloat(
      window.getComputedStyle(containerEl, null).getPropertyValue('padding-left') || 0,
    );
    const paddingTop = parseFloat(
      window.getComputedStyle(containerEl, null).getPropertyValue('padding-top') || 0,
    );
 
    const sourceNodeX = sourceNodeCoordinates.right - containerCoordinates.x - paddingLeft;
    const sourceNodeY =
      sourceNodeCoordinates.top -
      containerCoordinates.y -
      paddingTop +
      sourceNodeCoordinates.height / 2;
    const targetNodeX = targetNodeCoordinates.x - containerCoordinates.x - paddingLeft;
    const targetNodeY =
      targetNodeCoordinates.y -
      containerCoordinates.y -
      paddingTop +
      sourceNodeCoordinates.height / 2;
 
    const sourceNodeLeftX = sourceNodeCoordinates.left - containerCoordinates.x - paddingLeft;
 
    // If the source and target X values are the same,
    // it means the nodes are in the same column so we
    // want to start the line on the left of the pill
    // instead of the right to have a nice curve.
    const firstPointCoordinateX = sourceNodeLeftX === targetNodeX ? sourceNodeLeftX : sourceNodeX;
 
    // First point
    path.moveTo(firstPointCoordinateX, sourceNodeY);
 
    // Make cross-stages lines a straight line all the way
    // until we can safely draw the bezier to look nice.
    // The adjustment number here is a magic number to make things
    // look nice and should change if the padding changes. This goes well
    // with gl-px-9 which we translate with 100px here.
    const straightLineDestinationX = targetNodeX - 100;
    const controlPointX = straightLineDestinationX + (targetNodeX - straightLineDestinationX) / 2;
 
    Iif (straightLineDestinationX > firstPointCoordinateX) {
      path.lineTo(straightLineDestinationX, sourceNodeY);
    }
 
    // Add bezier curve. The first 4 coordinates are the 2 control
    // points to create the curve, and the last one is the end point (x, y).
    // We want our control points to be in the middle of the line
    path.bezierCurveTo(
      controlPointX,
      sourceNodeY,
      controlPointX,
      targetNodeY,
      targetNodeX,
      targetNodeY,
    );
 
    return {
      ...link,
      source: sourceId,
      target: targetId,
      ref: createUniqueLinkId(sourceId, targetId),
      path: path.toString(),
    };
  });
};