diff --git a/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs b/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs index 1e170425..9227fcf9 100644 --- a/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs +++ b/src/Blazor.Diagrams.Core/Controls/Default/RemoveControl.cs @@ -23,18 +23,44 @@ 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) + 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: diagram.Nodes.Remove(node); - break; + return; + case BaseLinkModel link: diagram.Links.Remove(link); - break; + return; + } + } + + private static async ValueTask ShouldDeleteModel(Diagram diagram, Model model) + { + if (model.Locked) + { + return false; } - return ValueTask.CompletedTask; + 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 new file mode 100644 index 00000000..1b2201ef --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Controls/RemoveControlTests.cs @@ -0,0 +1,272 @@ +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); + } + + [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); + } + + } +}