From 6321a2951fed858f8fad972c31bb4b079bd563a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Merlin=20Z=C3=B6bl?= <41567572+K0369@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:46:12 +0200 Subject: [PATCH 1/3] Adding check for "ShouldDelete"-constraint to remove control --- .../Controls/Default/RemoveControl.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs b/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs index 1e1704256..b9dacfc8a 100644 --- a/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs +++ b/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs @@ -23,18 +23,25 @@ public RemoveControl(IPositionProvider positionProvider) public override Point? GetPosition(Model model) => _positionProvider.GetPosition(model); - public override ValueTask OnPointerDown(Diagram diagram, Model model, PointerEventArgs _) + public override async ValueTask OnPointerDown(Diagram diagram, Model model, PointerEventArgs _) { switch (model) { + case NodeModel node: - diagram.Nodes.Remove(node); + if (await diagram.Options.Constraints.ShouldDeleteNode.Invoke(node)) + { + diagram.Nodes.Remove(node); + } break; case BaseLinkModel link: - diagram.Links.Remove(link); + if (await diagram.Options.Constraints.ShouldDeleteLink.Invoke(link)) + { + diagram.Links.Remove(link); + } break; - } - return ValueTask.CompletedTask; + + } } } \ No newline at end of file From 58bcdb33d93706c02d3b694cc86422472c9acf94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Merlin=20Z=C3=B6bl?= <41567572+K0369@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:47:30 +0200 Subject: [PATCH 2/3] Adding unit tests --- .../Controls/RemoveControlTests.cs | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs diff --git a/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs b/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs new file mode 100644 index 000000000..e24fce0ba --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs @@ -0,0 +1,179 @@ +using Blazor.Diagrams.Core.Controls.Default; +using Blazor.Diagrams.Core.Events; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Blazor.Diagrams.Core.Tests.Controls +{ + public class RemoveControlTests + { + public PointerEventArgs PointerEventArgs + => new(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true); + + [Fact] + public async Task OnPointerDown_NoConstraints_RemovesNode() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram(); + var nodeMock = new Mock(Point.Zero); + var node = diagram.Nodes.Add(nodeMock.Object); + + // Act + await removeControl.OnPointerDown(diagram, node, PointerEventArgs); + + // Assert + Assert.Empty(diagram.Nodes); + } + + [Fact] + public async Task OnPointerDown_ShouldDeleteNodeTrue_RemovesNode() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram( + new Options.DiagramOptions + { + Constraints = + { + ShouldDeleteNode = (node) => ValueTask.FromResult(true) + } + }); + var nodeMock = new Mock(Point.Zero); + var node = diagram.Nodes.Add(nodeMock.Object); + + // Act + await removeControl.OnPointerDown(diagram, node, PointerEventArgs); + + // Assert + Assert.Empty(diagram.Nodes); + } + + [Fact] + public async Task OnPointerDown_ShouldDeleteNodeFalse_KeepsNode() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram( + new Options.DiagramOptions + { + Constraints = + { + ShouldDeleteNode = (node) => ValueTask.FromResult(false) + } + }); + var nodeMock = new Mock(Point.Zero); + var node = diagram.Nodes.Add(nodeMock.Object); + + // Act + await removeControl.OnPointerDown(diagram, node, PointerEventArgs); + + // Assert + Assert.Contains(node, diagram.Nodes); + } + + [Fact] + public async Task OnPointerDown_NoConstraints_RemovesLink() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram(); + + var node1 = new NodeModel(new Point(50, 50)); + var node2 = new NodeModel(new Point(300, 300)); + diagram.Nodes.Add(new[] { node1, node2 }); + node1.AddPort(PortAlignment.Right); + node2.AddPort(PortAlignment.Left); + + var link = new LinkModel( + node1.GetPort(PortAlignment.Right)!, + node2.GetPort(PortAlignment.Left)! + ); + + diagram.Links.Add(link); + + // Act + await removeControl.OnPointerDown(diagram, link, PointerEventArgs); + + // Assert + Assert.Empty(diagram.Links); + } + + [Fact] + public async Task OnPointerDown_ShouldDeleteLinkTrue_RemovesLink() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram( + new Options.DiagramOptions + { + Constraints = + { + ShouldDeleteLink = (node) => ValueTask.FromResult(true) + } + }); + + var node1 = new NodeModel(new Point(50, 50)); + var node2 = new NodeModel(new Point(300, 300)); + diagram.Nodes.Add(new[] { node1, node2 }); + node1.AddPort(PortAlignment.Right); + node2.AddPort(PortAlignment.Left); + + var link = new LinkModel( + node1.GetPort(PortAlignment.Right)!, + node2.GetPort(PortAlignment.Left)! + ); + + diagram.Links.Add(link); + + // Act + await removeControl.OnPointerDown(diagram, link, PointerEventArgs); + + // Assert + Assert.Empty(diagram.Links); + } + + [Fact] + public async Task OnPointerDown_ShouldDeleteLinkFalse_KeepsLink() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram( + new Options.DiagramOptions + { + Constraints = + { + ShouldDeleteLink = (node) => ValueTask.FromResult(false) + } + }); + + var node1 = new NodeModel(new Point(50, 50)); + var node2 = new NodeModel(new Point(300, 300)); + diagram.Nodes.Add(new[] { node1, node2 }); + node1.AddPort(PortAlignment.Right); + node2.AddPort(PortAlignment.Left); + + var link = new LinkModel( + node1.GetPort(PortAlignment.Right)!, + node2.GetPort(PortAlignment.Left)! + ); + + diagram.Links.Add(link); + + // Act + await removeControl.OnPointerDown(diagram, link, PointerEventArgs); + + // Assert + Assert.Contains(link, diagram.Links); + } + + } +} From e58624574dfab9f98276bbf3119cf1ebf53b9920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Merlin=20Z=C3=B6bl?= Date: Thu, 26 Oct 2023 10:03:32 +0200 Subject: [PATCH 3/3] Adding functionality for GroupModel Removal to RemoveControl --- .../Controls/Default/RemoveControl.cs | 43 ++++++--- .../Controls/RemoveControlTests.cs | 93 +++++++++++++++++++ 2 files changed, 124 insertions(+), 12 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs b/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs index b9dacfc8a..9227fcf98 100644 --- a/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs +++ b/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs @@ -25,23 +25,42 @@ public RemoveControl(IPositionProvider positionProvider) public override async ValueTask OnPointerDown(Diagram diagram, Model model, PointerEventArgs _) { - switch (model) + if (await ShouldDeleteModel(diagram, model)) { + DeleteModel(diagram, model); + } + } + private static void DeleteModel(Diagram diagram, Model model) + { + switch (model) + { + case GroupModel group: + diagram.Groups.Delete(group); + return; case NodeModel node: - if (await diagram.Options.Constraints.ShouldDeleteNode.Invoke(node)) - { - diagram.Nodes.Remove(node); - } - break; + diagram.Nodes.Remove(node); + return; + case BaseLinkModel link: - if (await diagram.Options.Constraints.ShouldDeleteLink.Invoke(link)) - { - diagram.Links.Remove(link); - } - break; + diagram.Links.Remove(link); + return; + } + } - + private static async ValueTask ShouldDeleteModel(Diagram diagram, Model model) + { + if (model.Locked) + { + return false; } + + return model switch + { + GroupModel group => await diagram.Options.Constraints.ShouldDeleteGroup.Invoke(group), + NodeModel node => await diagram.Options.Constraints.ShouldDeleteNode.Invoke(node), + BaseLinkModel link => await diagram.Options.Constraints.ShouldDeleteLink.Invoke(link), + _ => false, + }; } } \ No newline at end of file diff --git a/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs b/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs index e24fce0ba..1b2201ef5 100644 --- a/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs +++ b/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs @@ -175,5 +175,98 @@ public async Task OnPointerDown_ShouldDeleteLinkFalse_KeepsLink() Assert.Contains(link, diagram.Links); } + [Fact] + public async Task OnPointerDown_NoConstraints_RemovesGroup() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram(); + + var node1 = new NodeModel(new Point(50, 50)); + var node2 = new NodeModel(new Point(300, 300)); + diagram.Nodes.Add(new[] { node1, node2 }); + node1.AddPort(PortAlignment.Right); + node2.AddPort(PortAlignment.Left); + + var group = new GroupModel(new[] { node1, node2 }); + + + diagram.Groups.Add(group); + + // Act + await removeControl.OnPointerDown(diagram, group, PointerEventArgs); + + // Assert + Assert.Empty(diagram.Groups); + Assert.Empty(diagram.Nodes); + } + + [Fact] + public async Task OnPointerDown_ShouldDeleteGroupTrue_RemovesGroup() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram( + new Options.DiagramOptions + { + Constraints = + { + ShouldDeleteGroup = (node) => ValueTask.FromResult(true) + } + }); + + var node1 = new NodeModel(new Point(50, 50)); + var node2 = new NodeModel(new Point(300, 300)); + diagram.Nodes.Add(new[] { node1, node2 }); + node1.AddPort(PortAlignment.Right); + node2.AddPort(PortAlignment.Left); + + var group = new GroupModel(new[] { node1, node2 }); + + + diagram.Groups.Add(group); + + // Act + await removeControl.OnPointerDown(diagram, group, PointerEventArgs); + + // Assert + Assert.Empty(diagram.Groups); + Assert.Empty(diagram.Nodes); + } + + [Fact] + public async Task OnPointerDown_ShouldDeleteGroupFalse_KeepsGroup() + { + // Arrange + RemoveControl removeControl = new(0, 0); + Diagram diagram = new TestDiagram( + new Options.DiagramOptions + { + Constraints = + { + ShouldDeleteGroup = (node) => ValueTask.FromResult(false) + } + }); + + var node1 = new NodeModel(new Point(50, 50)); + var node2 = new NodeModel(new Point(300, 300)); + diagram.Nodes.Add(new[] { node1, node2 }); + node1.AddPort(PortAlignment.Right); + node2.AddPort(PortAlignment.Left); + + var group = new GroupModel(new[] { node1, node2 }); + + + diagram.Groups.Add(group); + + // Act + await removeControl.OnPointerDown(diagram, group, PointerEventArgs); + + // Assert + Assert.Contains(group, diagram.Groups); + Assert.Contains(node1, diagram.Nodes); + Assert.Contains(node2, diagram.Nodes); + } + } }