Move nodes out of the way when group expands

Here’s a start. But it could be much smarter, which I’ll leave for you to implement.

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          "undoManager.isEnabled": true
        });

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        $(go.Shape,
          { fill: "white" },
          new go.Binding("fill", "color")),
        $(go.TextBlock,
          { margin: 8 },
          new go.Binding("text", "key"))
      );
    
    myDiagram.groupTemplate =
      $(go.Group, "Auto",
        $(go.Shape, { fill: "transparent", strokeWidth: 3 },
          new go.Binding("stroke", "color"),
          new go.Binding("fill", "color", go.Brush.lighten)),
        $(go.Placeholder, { alignment: go.Spot.TopLeft, padding: 15 }),
        $("SubGraphExpanderButton", { alignment: go.Spot.TopLeft }),
        {
          minSize: new go.Size(50, 50),
          subGraphExpandedChanged: function(grp) {
            if (!grp.isSubGraphExpanded) return;
            shiftNodes(grp);
          },
          selectionChanged: function(grp) {
            grp.diagram.commit(function(diag) {
              var lay = grp.isSelected ? "Foreground" : "";
              grp.layerName = lay;
              grp.findSubGraphParts().each(function(x) { x.layerName = lay; });
            }, null);
          }
        }
      );

  function shiftNodes(part) {
    part.ensureBounds();
    var b = part.actualBounds;
    var diagram = part.diagram;
    if (diagram === null) return;
    var overlaps = diagram.findObjectsIn(b,
          function(x) { var p = x.part; return (p.isTopLevel && p instanceof go.Node) ? p : null; },
          function(node) { return node !== part && !node.isMemberOf(part); },
          true);
    var dx = 0;
    var dy = 0;
    var shiftsXY = new go.Set();
    var shiftsX = new go.Set();
    var shiftsY = new go.Set();
    overlaps.each(function(node) {
      var r = node.actualBounds;
      if (r.contains(b.right, b.bottom)) {
        dx = Math.max(dx, b.right - r.left);
        dy = Math.max(dy, b.bottom - r.top);
        shiftsXY.add(node);
      } else if (b.contains(r.left, r.bottom)) {
        dx = Math.max(dx, b.right - r.left);
        shiftsX.add(node);
      } else if (b.contains(r.right, r.top)) {
        dy = Math.max(dy, b.bottom - r.top);
        shiftsY.add(node);
      }
    });
    if (dx > 0) diagram.moveParts(shiftsX, new go.Point(dx+10, 0), false);
    if (dy > 0) diagram.moveParts(shiftsY, new go.Point(0, dy+10), false);
    if (dx > 0 && dy > 0) diagram.moveParts(shiftsXY, new go.Point(dx+10, dy+10), false);
  }

  myDiagram.model = new go.GraphLinksModel(
    [
      { key: 1, color: "lightblue", isGroup: true },
      { key: 2, color: "orange", isGroup: true },
      { key: 3, color: "lightgreen", isGroup: true },
      { key: 4, color: "pink", isGroup: true },
      { group: 1 },
      { group: 1 },
      { group: 1 },
      { group: 1 },
      { group: 2 },
      { group: 2 },
      { group: 2 },
      { group: 3 },
      { group: 3 },
      { group: 4 },
      { group: 4 },
    ],
    [
    ]);
  }
  </script>
</head>
<body onload="init()">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
</body>
</html>