Unable to simulate context menu click using Robot.js

Hi

We are using go js 2.1.4 and in our diagram every node has a context menu, which works with mouse left click. We already disabled it on mouse right clicks. When user does a mouse left click, it works as expected.

Below a sample diagram with nodes and context menu:

But when we try to simulate this left click event, using Robot.js, it doesn’t open it, though it invokes the click handler that we defined for the context menu graph object (vertical ellipsis):

function(e, menuButton) {
     e.diagram.commandHandler.showContextMenu(menuButton.part);
};

I also noticed that, it doesn’t seem to be working in this sample as well: https://gojs.net/latest/extensions/Robot.html, i.e., when we add the node, “Alpha” to the diagram and click on “Context Menu Click Alpha” it doesn’t launch context menu for that node. I might be missing something here.

Are you saying that when you set a breakpoint in the function that you quoted above, the debugger does stop there and call showContextMenu on the expected “Alpha” Node, but no menu is shown?

If so, what is the sequence of Robot calls that you make to simulate the click?

Yes, it does call the above click handler. And below is the sequence:

//item the graph object which is clicked. This would the vertical ellipsis in case of context menu
// Get the bounds of the item and the scale
	const bounds = item.getDocumentBounds();

	// Click on item
	const robot = new Robot(diagram);
	robot.mouseDown(bounds.x + 10, bounds.y + 10, 0, {});
	robot.mouseUp(bounds.x + 10, bounds.y + 10, 100, {});

The same handler works in simulating mouse click on other types of graph objects like buttons, text blocks that we have on nodes/ edges.

What is the item’s actualBounds? Maybe a 10,10 offset to that x,y point misses the Shape or Picture or whatever it is. Just use item.actualBounds.center instead.

It didn’t work. Also I noticed similar issue in this sample as well: https://gojs.net/latest/extensions/Robot.html
When I tried clicking on "Context Menu Click Alpha” it didn’t launch context menu for that node. Only difference is that this sample is simulating mouse right click, where as we simulated the left click as we disabled right click for launching node’s context menu

What is the item’s actualBounds ?

{x: 192.9677419354839, y: 7, width: 16, height: 16, s: true}

OK, so it is wide and tall enough for the 10,10 offset.
But is it a Shape, and if so, what is its background value?
If the shape’s background is null (the default value) the click might still be missing the shape.

It’s a Picture with height and width as 16 which is inside an Auto panel.

Wait – what is menuButton in your code? Is it your vertical-three-dot Picture in a Node, or is it some object in your context menu?

And if it is the latter, is the context menu an Adornment? Check the value of menuButton.part.

It’s the Auto panel containing the vertical-three-dot picture.

I made a copy of the extensions/Robot.html sample, and made some changes to be like what you are talking about.

First I modified the node template to add a context-menu-showing button, which I implemented using a solid square Shape (the implementation details shouldn’t matter):

            nodeTemplate:
              $(go.Node, "Auto",
                {
                  click: nodeClicked,
                  doubleClick: nodeClicked,
                  contextMenu:
                    $("ContextMenu",
                      $("ContextMenuButton",
                        $(go.TextBlock, "Properties"),
                        { click: showProperties })
                    )
                },
                $(go.Shape, "Rectangle",
                  { fill: "lightgray" },
                  { portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" }),
                $(go.TextBlock,
                  { margin: 18 },
                  new go.Binding("text", "key")),
                $(go.Shape,
                  {
                    name: "BUTTON",
                    width: 16, height: 16, alignment: go.Spot.Right,
                    click: function(e, menuButton) {
                      e.diagram.commandHandler.showContextMenu(menuButton.part);
                    }
                  })),

Then I modified the clickLambda function that uses the Robot in the sample:

    function clickLambda() {
      var lambda = myDiagram.findNodeForKey("Lambda");
      if (lambda === null) return;
      var button = lambda.findObject("BUTTON");
      var loc = button.getDocumentBounds().center;

      // click on Lambda
      robot.mouseDown(loc.x, loc.y, 0, {});
      robot.mouseUp(loc.x, loc.y, 100, {});

      // Clicking is just a sequence of input events.
      // There is no command in CommandHandler for such a basic gesture.
    }

Then when I ran the sample, the first thing I did was click on the “Click Lambda” HTML button. As expected, it showed the “Lambda” node’s context menu.

So I do not encounter any problem in showing the node’s context menu by robot-clicking the 16x16 object in the node.

However, I did run into one unexpected side-effect: the whole node was also clicked. At first I thought the problem was that the shape’s click handler didn’t set InputEvent.handled to true, but that wasn’t enough. The behavior is that CommandHandler.showContextMenu basically clicks at the middle of the object that has the context menu defined – the whole node in this case. This is normally desired so that the normal click behavior of the object happens before the context menu is shown, in case for example that the node is expected to be selected by any of the context menu commands. But maybe in your situation you don’t want that.

Only two differences that I see are:

  1. Ours is a custom context menu implementation using HTMLInfo
  2. I have set isActionable property to true on the Auto Panel that holds vertical-three-dot picture object. This is needed as we don’t want to select a node, when user directly clicks on context menu.

Not sure whether these could be causing the issue in our case.

I modified the node template to set isActionable and use actionUp, as I understand what you are doing in your template. Here’s my code:

            nodeTemplate:
              $(go.Node, "Auto",
                {
                  //click: nodeClicked,
                  //doubleClick: nodeClicked,
                  contextMenu:
                    $("ContextMenu",
                      $("ContextMenuButton",
                        $(go.TextBlock, "Properties"),
                        { click: showProperties })
                    )
                },
                $(go.Shape, "Rectangle",
                  { fill: "lightgray" },
                  { portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" }),
                $(go.TextBlock,
                  { margin: 18 },
                  new go.Binding("text", "key")),
                $(go.Shape,
                  {
                    name: "BUTTON",
                    width: 16, height: 16, alignment: go.Spot.Right,
                    isActionable: true,
                    actionUp: function(e, menuButton) {
                      e.diagram.commandHandler.showContextMenu(menuButton.part);
                    }
                  })),

But the behavior using Robot correctly showed the context menu, although this time without selecting the node.

I suppose I could try using an HTML context menu instead of a GoJS one. I modified the node template further:

              $(go.Node, "Auto",
                {
                  contextMenu:
                    $(go.HTMLInfo,
                      {
                        show: function(obj, diag, tool) {
                          console.log("showing context menu for " + obj.part.key)
                        }
                      }
                    )
                },
                . . .

Again, it worked as expected.