feat(centralui): NodeBrowserDialog.SelectableNodeClasses — opt-in Object node selection (default unchanged)
Claude-Session: https://claude.ai/code/session_01G2GHPnbbKGvyyfbr2XBRVH
This commit is contained in:
+44
@@ -29,6 +29,9 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
new AddressSpaceMatch(
|
||||
new BrowseNode("ns=2;s=Pump1.Speed", "Speed", BrowseNodeClass.Variable, HasChildren: false, DataType: "Double"),
|
||||
Path: "Devices/Pump1/Speed"),
|
||||
new AddressSpaceMatch(
|
||||
new BrowseNode("ns=2;s=Pump1", "Pump1", BrowseNodeClass.Object, HasChildren: true),
|
||||
Path: "Devices/Pump1"),
|
||||
},
|
||||
CapReached: false, Failure: null));
|
||||
}
|
||||
@@ -100,4 +103,45 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
Assert.Equal("Area1.Pump.Speed", captured!.NodeId);
|
||||
Assert.Equal("Float", captured.DataType);
|
||||
}
|
||||
|
||||
// SelectableNodeClasses (design 2026-08-21): the create page needs to pick a
|
||||
// module's CONTAINER object, not just a leaf tag. Default stays Variable-only
|
||||
// so every existing caller is unaffected.
|
||||
[Fact]
|
||||
public void Object_node_is_not_selectable_by_default()
|
||||
{
|
||||
var cut = Render<NodeBrowserDialog>(p => p
|
||||
.Add(c => c.SiteId, "plant-a")
|
||||
.Add(c => c.ConnectionName, "PLC-OPC"));
|
||||
cut.InvokeAsync(() => cut.Instance.ShowAsync("plant-a", "PLC-OPC", null));
|
||||
cut.Render();
|
||||
cut.Find("[data-test=node-search-input]").Input("Pump1");
|
||||
cut.Find("[data-test=node-search-button]").Click();
|
||||
|
||||
// Only the Variable renders a select button; the Object is plain text.
|
||||
Assert.Single(cut.FindAll("[data-test=node-search-result] button.btn-link"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Object_node_is_selectable_when_listed()
|
||||
{
|
||||
BrowseNode? captured = null;
|
||||
var cut = Render<NodeBrowserDialog>(p => p
|
||||
.Add(c => c.SiteId, "plant-a")
|
||||
.Add(c => c.ConnectionName, "PLC-OPC")
|
||||
.Add(c => c.SelectableNodeClasses, (IReadOnlyCollection<BrowseNodeClass>)new[] { BrowseNodeClass.Object, BrowseNodeClass.Variable })
|
||||
.Add(c => c.OnNodeSelected, EventCallback.Factory.Create<BrowseNode>(this, n => captured = n)));
|
||||
cut.InvokeAsync(() => cut.Instance.ShowAsync("plant-a", "PLC-OPC", null));
|
||||
cut.Render();
|
||||
cut.Find("[data-test=node-search-input]").Input("Pump1");
|
||||
cut.Find("[data-test=node-search-button]").Click();
|
||||
|
||||
var buttons = cut.FindAll("[data-test=node-search-result] button.btn-link");
|
||||
Assert.Equal(2, buttons.Count);
|
||||
buttons.First(b => b.TextContent.Trim() == "Pump1").Click();
|
||||
cut.Find(".modal-footer .btn-primary").Click();
|
||||
|
||||
Assert.Equal("ns=2;s=Pump1", captured!.NodeId);
|
||||
Assert.Equal(BrowseNodeClass.Object, captured.NodeClass);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user