Vertical and Horizontal scrollbar in group's placeholder with TreeLayout in Placeholder

Hi,
I have group template with TreeLayout in its placeholder. I fetch data from server and it displays a huge tree without scrollbars as you can see in image below. My requirement is that I want to restrict the size of my placeholder (containing all my tree nodes) and provide scroll bar(both horizontal and vertical) if in case it exceeds the desired size.
I gone through ScrollingGroup sample that has linear nodes unlike my requirement.
Any help would be appreciated.

Are you working with Scrolling Group with SubGraphExpanderButton - GoJS - Northwoods Software (nwoods.com) ?

Yes Walter. Any solution that i can have scrollbar in it?

This shows scrollbars when the Group is selected.

The user can also scroll the contents of the group using mouse wheel (with Shift if they want to scroll horizontally) when the mouse is inside the group.

The Group is also resizable, so that you can play with the clipping area explicitly, but that isn’t required.

<!DOCTYPE html>
<html>
<head>
  <title>Clipped Groups</title>
  <!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://unpkg.com/gojs"></script>
  <!-- just use AutoRepeatButton from this file: -->
  <script src="https://unpkg.com/gojs/extensions/ScrollingTable.js"></script>
  <script id="code">

  function GroupTreeLayout() {
    go.TreeLayout.call(this);
  }
  go.Diagram.inherit(GroupTreeLayout, go.TreeLayout);

  GroupTreeLayout.prototype.initialOrigin = function() {
    if (this.group) {
      var sized = this.group.findObject("SIZED");
      if (sized) return sized.getDocumentPoint(new go.Spot(0, 0, 4, 4));  // top-left margin
    }
    return this.arrangementOrigin;
  }

  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv",  // create a Diagram for the DIV HTML element
        {
          "InitialLayoutCompleted": function(e) {
            e.diagram.nodes.each(function(g) {
              if (g instanceof go.Group) {
                var sized = g.findObject("SIZED");
                if (sized) {
                  updateInteraction(g, sized.getDocumentBounds());
                }
              }
            })
          },
          "resizingTool.dragsMembers": false,
          // support mouse scrolling of subgraphs
          scroll: function(unit, dir, dist) {  // override Diagram.scroll
            if (!dist) dist = 1;
            var it = this.findPartsAt(this.lastInput.documentPoint).iterator;
            while (it.next()) {
              var grp = it.value;
              if (grp instanceof go.Group) {  // if the mouse is in a Group, scroll it
                scrollGroup(grp, unit, dir, dist);
                return;
              }
            }
            // otherwise, scroll the viewport normally
            go.Diagram.prototype.scroll.call(this, unit, dir, dist);
          }
          // alternate design: scroll subgraph of selected Group
          // scroll: function(unit, dir, dist) {  // override Diagram.scroll
          //   if (!dist) dist = 1;
          //   var grp = this.selection.first();
          //   if (grp instanceof go.Group) {  // if a Group is selected, scroll its contents
          //     scrollGroup(grp, unit, dir, dist);
          //   } else {  // otherwise, scroll the viewport normally
          //     go.Diagram.prototype.scroll.call(this, unit, dir, dist);
          //   }
          // }
        });

    // A function like Diagram.scroll that scrolls the subgraph of a Group
    function scrollGroup(grp, unit, dir, dist) {
      if (!(grp instanceof go.Group)) return;
      var diag = grp.diagram;
      if (!diag) return;
      var sized = grp.findObject("SIZED");
      if (!sized) sized = grp;
      var bnds = diag.computePartsBounds(grp.memberParts);
      var view = sized.getDocumentBounds();
      var dx = 0;
      var dy = 0;
      switch (unit) {
        case "pixel":
          switch (dir) {
            case "up": dy = -10; break;
            case "down": dy = 10; break;
            case "left": dx = -10; break;
            case "right": dx = 10; break;
          }
          break;
        case "line":
          switch (dir) {
            case "up": dy = -20; break;
            case "down": dy = 20; break;
            case "left": dx = -20; break;
            case "right": dx = 20; break;
          }
          break;
        case "page":
          switch (dir) {
            case "up": dy = Math.min(-10, -sized.actualBounds.height + 10); break;
            case "down": dy = Math.max(10, sized.actualBounds.height - 10); break;
            case "left": dx = Math.min(-10, -sized.actualBounds.width + 10); break;
            case "right": dx = Math.max(10, sized.actualBounds.width - 10); break;
          }
          break;
      }
      if (dx > 0) dx = Math.min(dx, view.left + 4 - bnds.left);  // top-left margin
      else if (dx < 0 && view.right-2 > bnds.right) dx = 0;
      if (dy > 0) dy = Math.min(dy, view.top + 4 - bnds.top);  // top-left margin
      else if (dy < 0 && view.bottom-2 > bnds.bottom) dy = 0;
      if (dx !== 0 || dy !== 0) {
        diag.commit(function(diag) {
          diag.moveParts(grp.memberParts, new go.Point(dx, dy), true);
        });
        updateInteraction(grp, view);
      }
    }

    // Make sure user cannot click or select any node that is completely outside;
    // but note that a node partly inside and partly outside, will be clickable outside.
    // SIZEDB must be a Rect in document coordinates.
    function updateInteraction(grp, sizedb) {
      grp.memberParts.each(function(part) {
        part.pickable = part.selectable = sizedb.intersectsRect(part.actualBounds);
      });
    }

    myDiagram.nodeTemplate =
      $(go.Node, go.Panel.Auto,
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.Shape, { fill: "lightgray" },
          new go.Binding("fill", "color")),
        $(go.TextBlock, { margin: 4 },
          new go.Binding("text")));

    myDiagram.linkTemplate =
      $(go.Link,
        {
          routing: go.Link.Orthogonal,
          fromSpot: new go.Spot(0.25, 1),
          toSpot: go.Spot.Left
        },
        $(go.Shape)
      )

    myDiagram.groupTemplate =
      $(go.Group, "Table",
        {
          selectionObjectName: "SIZED",
          locationObjectName: "SIZED",
          resizable: true, resizeObjectName: "SIZED",
          layout: $(GroupTreeLayout, {
            alignment: go.TreeLayout.AlignmentStart,
            nodeIndent: 2,
            nodeIndentPastParent: 1,
            nodeSpacing: 2,
            layerSpacing: 10,
            layerSpacingParentOverlap: 0.75,
            setsPortSpot: false,
            setsChildPortSpot: false
          }),
          isClipping: true  // experimental property for non-Spot Groups
        },
        new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.Shape,
          {
            name: "SIZED",
            row: 1, column: 1,
            minSize: new go.Size(20, 20),
            fill: "rgba(0,0,0,0.05)", stroke: "blue", strokeWidth: 2
          },
          new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)),
        $(go.TextBlock,
          { row: 0, column: 1, alignment: go.Spot.Left, font: "bold 14pt sans-serif" },
          new go.Binding("text"))
      );

    myDiagram.groupTemplate.selectionAdornmentTemplate =
      $(go.Adornment, "Table",
        $(go.Placeholder, { row: 1, column: 1 }),
        $(go.RowColumnDefinition, { row: 2, background: "whitesmoke" }),
        $(go.RowColumnDefinition, { column: 2, background: "whitesmoke" }),
        // Scrolling buttons
        $("AutoRepeatButton",
          {
            row: 2, column: 1, alignment: go.Spot.Left,
            click: function(e, but) { scrollGroup(but.part.adornedPart, "pixel", "right"); }
          },
          $(go.Shape, "TriangleLeft",
            { stroke: null, desiredSize: new go.Size(6, 8) })),
        $("AutoRepeatButton",
          {
            row: 2, column: 1, alignment: go.Spot.Right,
            click: function(e, but) { scrollGroup(but.part.adornedPart, "pixel", "left"); }
          },
          $(go.Shape, "TriangleRight",
            { stroke: null, desiredSize: new go.Size(6, 8) })),
        $("AutoRepeatButton",
          {
            row: 1, column: 2, alignment: go.Spot.Top,
            click: function(e, but) { scrollGroup(but.part.adornedPart, "pixel", "down"); }
          },
          $(go.Shape, "TriangleUp",
            { stroke: null, desiredSize: new go.Size(8, 6) })),
        $("AutoRepeatButton",
          {
            row: 1, column: 2, alignment: go.Spot.Bottom,
            click: function(e, but) { scrollGroup(but.part.adornedPart, "pixel", "up"); }
          },
          $(go.Shape, "TriangleDown",
            { stroke: null, desiredSize: new go.Size(8, 6) }))
      );

    var nodeDataArray = [
      { key: 0, isGroup: true, text: "Group", loc: "0 0", size: "200 50" },
      { key: 1, text: "Concept Maps", group: 0 },
      { key: 2, text: "Organized Knowledge", group: 0 },
      { key: 3, text: "Context Dependent", group: 0 },
      { key: 4, text: "Concepts", group: 0 },
      { key: 5, text: "Propositions", group: 0 },
      { key: 6, text: "Associated Feelings or Affect", group: 0 }
    ];
    var linkDataArray = [
      { from: 1, to: 2 },
      { from: 1, to: 3 },
      { from: 1, to: 4 },
      { from: 4, to: 5 },
      { from: 4, to: 6 }
    ];
    myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
  }
  </script>
</head>
<body onload="init()">
<div id="sample">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:500px; min-width: 200px"></div>
</div>
</body>
</html>

Thanks Walter. Will try and let you know.