Proper panel nesting in order to make it collapsible

Hello again))
Previously I created a label with the following configuration:

const labelNode = $(
  go.Node,
  'Viewbox',
  {
    resizable: true,
    resizeObjectName: 'labelPanel',
  },
  new go.Binding('location', 'position', go.Point.parse),
  new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),
  $(go.Panel, 'Vertical', new go.Binding('itemArray', 'properties'), {
    itemTemplate: $(
      go.Panel,
      'Vertical',
      {
        name: 'labelPanel',
        defaultStretch: go.GraphObject.Horizontal,
        background: 'transparent',
        padding: new go.Margin(2, 2, 10, 2),
        cursor: 'grab',
        alignment: go.Spot.Left,
      },
      $(go.TextBlock, new go.Binding('text', 'name', (t) => t.toUpperCase()), {
        textAlign: 'left',
        stroke: uiTheme.label,
        font: '12px Roboto',
        margin: new go.Margin(0, 0, 2, 0),
      }),
      $(go.TextBlock, new go.Binding('text', 'value'), {
        textAlign: 'left',
        stroke: uiTheme.label,
        font: '12px Roboto',
      }),
    ),
  }),
);

It gave me the following label:
3

Now we have another iteration, where it’s planned that the label will be expandable:
1

I planned to wrap the existing vertical panel into one more vertical panel and add a kind of a ‘header’ - a horizontal panel with a group name and button for collapsing before that panel with an array:

const labelNode = $(
  go.Node,
  'Viewbox',
  {
    resizable: true,
    resizeObjectName: 'labelPanel',
  },
  new go.Binding('location', 'position', go.Point.parse),
  new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),

  //extra wrapper
  $(go.Panel,
    'Vertical',
    // header
    $(go.Panel,
      'Horizontal',
      {
        alignment: go.Spot.Left,
      },
      // group name
      $(go.TextBlock,
        {
          name: 'GROUP_NAME',
          text: 'Some group name',
          alignment: go.Spot.Left,
          stroke: uiTheme.label,
          maxLines: 1,
        },
        // new go.Binding('text', 'serial')), will work with bindings later
      ),
       //expander itself
      $('PanelExpanderButton',
        'labelPanel',
        {
          alignment: go.Spot.TopLeft,
          alignmentFocus: go.Spot.TopLeft,
        }),
    ),

    //properties array
    $(go.Panel,
      'Vertical',
      new go.Binding('itemArray', 'properties'),
      new go.Binding("visible").makeTwoWay(),// added this

      {
        itemTemplate:
          $(go.Panel,
            'Vertical',
            {
              visible: true, // added this
              name: 'labelPanel',
              defaultStretch: go.GraphObject.Horizontal,
              background: 'transparent',
              padding: new go.Margin(2, 2, 10, 2),
              cursor: 'grab',
              alignment: go.Spot.Left,
            },
            $(go.TextBlock,
              new go.Binding('text', 'name', (t) => t.toUpperCase()),
              {
                textAlign: 'left',
                stroke: uiTheme.label,
                font: '12px Roboto',
                margin: new go.Margin(0, 0, 2, 0),
              }),
            $(go.TextBlock,
              new go.Binding('text', 'value'),
              {
                textAlign: 'left',
                stroke: uiTheme.label,
                font: '12px Roboto',
              }),
          ),
      }),// array end
  )
);

However, the button looks like a big black square and does nothing on clicking. Could you please advise me - is it possible to do it with nesting panels, or do I have to use a table, as in the example?

I’m not sure what you’re trying to do, but “Vertical” Panels cannot really stretch its contents vertically, and “Horizontal” Panels cannot really stretch its contents horizontally. So it might make sense to use a “Table” Panel, which is much more flexible.

Sorry, have forgotten to attach my current result:
2

As for what I’m trying to achieve -
something like in the example - GoJS Buttons -- Northwoods Software
However, I was wondering if to have this collapsible functionality I have to use a table panel, or can I combine nesting of vertical and horizontal panels?

Oh, if you want to use any of those predefined "…Button"s, they should work regardless of how you define your Node template. Of course, when using a “PanelExpanderButton” I would want to make sure that that button is not in the named Panel.

I think that sample uses “Table” Panels just so that they arrange naturally. Unless you want the hiding to be irreversible because the button has disappeared.

Well, here is the part I do not understand - placing a button into a collapsible panel makes it look and work as expected - collapsing on click

However, placing it into another panel - breaks everything:

Where do you want the button to be and what do you want it to show/hide?

If you put the button in the itemTemplate, then the button will be duplicated in each item panel and each button can only affect the visibility of a GraphObject within that item Panel. It does not make sense for it to be trying to find some named panel (actually, it can be any GraphObject) anywhere outside of that item panel.

If you put the button outside of the Panel with the itemTemplate, then it will only search for a named object outside of any item panel. After all, if you have several items in the data Array, there will be several item Panels, and each one will have its own named object such as your “labelPanel”. Which one should be hidden/shown? That’s why Panel.findObject stops searching the visual tree of GraphObjects when it gets to an item Panel.

I need to make the label display only name in the collapsed state:
image

In the expanded state, it should display a name, optionally units, and then a list of properties (min 1, max 3) with their values.
image

Sounds like you want to show/hide the whole Panel that holds all of the item Panels.
I.e. the panel whose Panel.itemArray is data bound.

well, yes, it’s what I was trying to do. Could you kindly advise me on what I need to change in this configuration to achieve such a result?

const labelNode = $(
  go.Node,
  'Viewbox',
  {
    resizable: true,
    resizeObjectName: 'labelPanel',
  },
  new go.Binding('location', 'position', go.Point.parse),
  new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),

  //wrapper
  $(go.Panel,
    'Vertical',
    // header
    $(go.Panel,
      'Horizontal',
      {
        alignment: go.Spot.Left,
      },
      // group name
      $(go.TextBlock,
        {
          name: 'GROUP_NAME',
          text: 'Some group name',
          alignment: go.Spot.Left,
          stroke: uiTheme.label,
          maxLines: 1,
        },
        // new go.Binding('text', 'serial')), will work with bindings later
      ),
      //expander
      $('PanelExpanderButton',
        'labelPanel',
        {
          alignment: go.Spot.TopLeft,
          alignmentFocus: go.Spot.TopLeft,
        }),
    ),

    //panel with properties array
    $(go.Panel,
      'Vertical',
      new go.Binding('itemArray', 'properties'),
      new go.Binding("visible").makeTwoWay(),//

      {
        itemTemplate:
          $(go.Panel,
            'Vertical',
            {
              visible: true, // 
              name: 'labelPanel',
              defaultStretch: go.GraphObject.Horizontal,
              background: 'transparent',
              padding: new go.Margin(2, 2, 10, 2),
              cursor: 'grab',
              alignment: go.Spot.Left,
            },
            $(go.TextBlock,
              new go.Binding('text', 'name', (t) => t.toUpperCase()),
              {
                textAlign: 'left',
                stroke: uiTheme.label,
                font: '12px Roboto',
                margin: new go.Margin(0, 0, 2, 0),
              }),
            $(go.TextBlock,
              new go.Binding('text', 'value'),
              {
                textAlign: 'left',
                stroke: uiTheme.label,
                font: '12px Roboto',
              }),
          ),
      }),// array end
  )
);

Add a setting of GraphObject.name on the Panel with the itemTemplate:

  {
    name: "labelPanel",
    itemTemplate: . . .
  }

You can remove that name from within the itemTemplate, if you still have it. Unless you need it for some other reason…

Yes, collapsing worked out, thank you!

7