diff --git a/COMETwebapp.Tests/Components/MultiModelEditor/ElementDefinitionTreeTestFixture.cs b/COMETwebapp.Tests/Components/MultiModelEditor/ElementDefinitionTreeTestFixture.cs new file mode 100644 index 00000000..2179be21 --- /dev/null +++ b/COMETwebapp.Tests/Components/MultiModelEditor/ElementDefinitionTreeTestFixture.cs @@ -0,0 +1,255 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2024 Starion Group S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The COMET WEB Community Edition is free software; you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// The COMET WEB Community Edition is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Components.MultiModelEditor +{ + using Bunit; + using Bunit.Rendering; + + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.Model; + using COMET.Web.Common.Model.Configuration; + using COMET.Web.Common.Services.ConfigurationService; + using COMET.Web.Common.Services.SessionManagement; + using COMET.Web.Common.Test.Helpers; + + using COMETwebapp.Components.MultiModelEditor; + using COMETwebapp.ViewModels.Components.MultiModelEditor; + using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows; + + using DevExpress.Blazor; + + using Microsoft.Extensions.DependencyInjection; + + using Moq; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class ElementDefinitionTreeTestFixture + { + private TestContext context; + private IRenderedComponent renderedComponent; + private ElementDefinitionTree tree; + private Mock elementDefinitionTreeViewModel; + + [SetUp] + public void SetUp() + { + this.context = new TestContext(); + this.context.ConfigureDevExpressBlazor(); + + var configuration = new Mock(); + configuration.Setup(x => x.ServerConfiguration).Returns(new ServerConfiguration()); + var elementDefinition = new ElementDefinition(); + + var row = new ElementDefinitionTreeRowViewModel + { + ElementName = "Test1", + ElementBase = elementDefinition, + OwnerShortName = "Owner", + IsTopElement = true + }; + + this.elementDefinitionTreeViewModel = new Mock(); + this.elementDefinitionTreeViewModel.Setup(x => x.Rows).Returns([row]); + + this.context.Services.AddSingleton(configuration.Object); + this.context.Services.AddSingleton(this.elementDefinitionTreeViewModel.Object); + this.context.Services.AddSingleton(); + + this.renderedComponent = this.context.RenderComponent(); + this.tree = this.renderedComponent.Instance; + } + + [TearDown] + public void TearDown() + { + this.context.CleanContext(); + } + + [Test] + public void VerifyComponent() + { + Assert.Multiple(() => + { + Assert.That(this.renderedComponent, Is.Not.Null); + Assert.That(this.tree, Is.Not.Null); + Assert.That(this.tree.ViewModel, Is.Not.Null); + }); + } + + [Test] + public void VerifyElementSelection() + { + ElementBaseTreeRowViewModel selectedModel = null; + + this.renderedComponent = this.context.RenderComponent(parameters => + { + parameters + .Add(p => p.SelectionChanged, model => selectedModel = model); + }); + + var treeView = this.renderedComponent.FindComponent(); + + Assert.Multiple(() => + { + Assert.That(treeView.Instance.GetSelectedNodeInfo(), Is.Null); + Assert.That(selectedModel, Is.Null); + }); + + var firstSourceRow = treeView.Find(".dxbl-treeview-item-container"); + firstSourceRow.Click(); + + Assert.Multiple(() => + { + Assert.That(treeView.Instance.GetSelectedNodeInfo(), Is.Not.Null); + Assert.That(selectedModel, Is.Not.Null); + }); + } + + [Test] + public void VerifyIsModelSelectionEnabled() + { + Assert.That(() => this.renderedComponent.FindComponent>(), Throws.TypeOf()); + + this.renderedComponent = this.context.RenderComponent(parameters => + { + parameters + .Add(p => p.IsModelSelectionEnabled, true); + }); + + Assert.That(this.renderedComponent.FindComponent>(), Is.Not.Null); + } + + [Test] + public void VerifyInitialIteration() + { + var iteration = new Iteration + { + Iid = Guid.NewGuid(), + IterationSetup = new IterationSetup + { + IterationNumber = 1, + Container = new EngineeringModelSetup + { + Iid = Guid.NewGuid(), + Name = "ModelName", + ShortName = "ModelShortName" + } + } + }; + + this.elementDefinitionTreeViewModel.VerifySet(x => x.Iteration = iteration, Times.Never); + + this.renderedComponent = this.context.RenderComponent(parameters => + { + parameters + .Add(p => p.InitialIteration, iteration); + }); + + this.elementDefinitionTreeViewModel.VerifySet(x => x.Iteration = iteration, Times.Once); + } + + [Test] + public void VerifyInitialIterationChangeFromViewModel() + { + var iteration1 = new Iteration + { + Iid = Guid.NewGuid(), + IterationSetup = new IterationSetup + { + IterationNumber = 1, + Container = new EngineeringModelSetup + { + Iid = Guid.NewGuid(), + Name = "ModelName", + ShortName = "ModelShortName" + } + } + }; + + var iteration2 = new Iteration + { + Iid = Guid.NewGuid(), + IterationSetup = new IterationSetup + { + IterationNumber = 2, + Container = iteration1.IterationSetup.Container + } + }; + + this.elementDefinitionTreeViewModel.VerifySet(x => x.Iteration = iteration1, Times.Never); + + this.renderedComponent = this.context.RenderComponent(parameters => + { + parameters + .Add(p => p.InitialIteration, iteration1); + }); + + this.elementDefinitionTreeViewModel.VerifySet(x => x.Iteration = iteration1, Times.Once); + this.elementDefinitionTreeViewModel.Setup(x => x.Iteration).Returns(iteration2); + + this.renderedComponent.Render(); + + Assert.That(this.renderedComponent.Instance.InitialIteration, Is.EqualTo(iteration2)); + } + + [Test] + public void VerifyDragIsNotAllowed() + { + var firstItem = this.renderedComponent.Find(".dxbl-text").FirstElementChild; + + Assert.Multiple(() => + { + Assert.That(firstItem, Is.Not.Null); + Assert.That(firstItem.InnerHtml, Contains.Substring("Test1")); + Assert.That(firstItem.Attributes.Length, Is.EqualTo(1)); + }); + } + + [Test] + public void VerifyDragIsAllowed() + { + this.renderedComponent = this.context.RenderComponent(parameters => + { + parameters + .Add(p => p.AllowDrag, true); + }); + + var firstItem = this.renderedComponent.Find(".dxbl-text").FirstElementChild; + + Assert.Multiple(() => + { + Assert.That(firstItem, Is.Not.Null); + Assert.That(firstItem.InnerHtml, Contains.Substring("Test1")); + Assert.That(firstItem.Attributes.Length, Is.EqualTo(9)); + }); + } + } +} diff --git a/COMETwebapp.Tests/ViewModels/Components/MultiModeleditor/ElementDefinitionTreeViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/MultiModeleditor/ElementDefinitionTreeViewModelTestFixture.cs index 1e7f71d4..c2b47041 100644 --- a/COMETwebapp.Tests/ViewModels/Components/MultiModeleditor/ElementDefinitionTreeViewModelTestFixture.cs +++ b/COMETwebapp.Tests/ViewModels/Components/MultiModeleditor/ElementDefinitionTreeViewModelTestFixture.cs @@ -140,7 +140,6 @@ public void VerifySelectIteration() Assert.That(this.viewModel.Description, Is.EqualTo("Please select a model")); Assert.That(this.viewModel.Iteration, Is.Null); Assert.That(this.viewModel.Iterations.Count, Is.EqualTo(2)); - Assert.That(this.viewModel.SelectedElementDefinition, Is.Null); Assert.That(this.viewModel.SelectedIterationData, Is.Null); Assert.That(this.viewModel.Rows.Count, Is.EqualTo(0)); }); @@ -153,7 +152,6 @@ public void VerifySelectIteration() Assert.That(this.viewModel.Description, Is.EqualTo(expectedIterationData.IterationName)); Assert.That(this.viewModel.Iteration, Is.EqualTo(this.iteration)); Assert.That(this.viewModel.Iterations.Count, Is.EqualTo(2)); - Assert.That(this.viewModel.SelectedElementDefinition, Is.Null); Assert.That(this.viewModel.SelectedIterationData, Is.EqualTo(expectedIterationData)); Assert.That(this.viewModel.Rows.Count, Is.EqualTo(2)); }); @@ -170,7 +168,6 @@ public void VerifyUpdatedElement() Assert.That(this.viewModel.Description, Is.EqualTo(expectedIterationData.IterationName)); Assert.That(this.viewModel.Iteration, Is.EqualTo(this.iteration)); Assert.That(this.viewModel.Iterations.Count, Is.EqualTo(2)); - Assert.That(this.viewModel.SelectedElementDefinition, Is.Null); Assert.That(this.viewModel.SelectedIterationData, Is.EqualTo(expectedIterationData)); Assert.That(this.viewModel.Rows.Count, Is.EqualTo(2)); }); @@ -198,7 +195,6 @@ public void VerifyRemoveAndAddElement() Assert.That(this.viewModel.Description, Is.EqualTo(expectedIterationData.IterationName)); Assert.That(this.viewModel.Iteration, Is.EqualTo(this.iteration)); Assert.That(this.viewModel.Iterations.Count, Is.EqualTo(2)); - Assert.That(this.viewModel.SelectedElementDefinition, Is.Null); Assert.That(this.viewModel.SelectedIterationData, Is.EqualTo(expectedIterationData)); Assert.That(this.viewModel.Rows.Count, Is.EqualTo(2)); }); diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor index 40e394bc..232e4d88 100644 --- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor +++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor @@ -21,7 +21,6 @@ -------------------------------------------------------------------------------> @using COMET.Web.Common.Model @using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows -@using CDP4JsonSerializer.JsonConverter @inject IJSRuntime JSRuntime
@@ -52,7 +51,7 @@ else
@@ -61,45 +60,29 @@ else var dataItem = (ElementBaseTreeRowViewModel)context.DataItem; var draggable = this.AllowDrag && this.AllowNodeDrag.Invoke(this, dataItem); var isTopElement = dataItem is ElementDefinitionTreeRowViewModel { IsTopElement: true }; - var style = isTopElement ? "font-weight-bold" : string.Empty; + var cssClass = isTopElement ? "font-weight-bold" : string.Empty; } @if (draggable) { -
- @dataItem.ElementName - - - @foreach (var categoryShortName in dataItem.ElementBase.GetAllCategories().Select(x => x.ShortName)) - { - - - - } -
+ } else { -
- @dataItem.ElementName - - - @foreach (var categoryShortName in dataItem.ElementBase.GetAllCategories().Select(x => x.ShortName)) - { - - - - } -
+ } + diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs index 0de0c78b..72bc27ff 100644 --- a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs +++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTree.razor.cs @@ -128,14 +128,14 @@ public partial class ElementDefinitionTree private object dragOverNode; /// - /// Gets or sets a value indicating that dropping a node is allowed for this + /// Holds a reference to dragover count due to nested child elements of the dragOverNode /// - public bool AllowNodeDrop { get; set; } + private int dragOverCounter; /// - /// A collection of + /// Gets or sets a value indicating that dropping a node is allowed for this /// - public IEnumerable Rows { get; set; } + public bool AllowNodeDrop { get; set; } /// /// Gets or sets the @@ -237,7 +237,14 @@ private async Task DragEnterAsync(object node) { if (this.AllowDrop) { + if (this.dragOverNode != node) + { + this.dragOverCounter = 0; + } + this.dragOverNode = node; + this.dragOverCounter++; + await this.OnDragEnter.InvokeAsync((this, node)); await this.OnCalculateDropIsAllowed.InvokeAsync(this); @@ -254,10 +261,15 @@ private async Task DragLeaveAsync(object node) { if (this.AllowDrop) { - this.dragOverNode = null; - await this.OnDragLeave.InvokeAsync((this, node)); - await this.OnCalculateDropIsAllowed.InvokeAsync(this); - this.Logger.LogDebug("DragLeave"); + this.dragOverCounter--; + + if (this.dragOverCounter <= 0) + { + this.dragOverNode = null; + await this.OnDragLeave.InvokeAsync((this, node)); + await this.OnCalculateDropIsAllowed.InvokeAsync(this); + this.Logger.LogDebug("DragLeave"); + } } } } diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor new file mode 100644 index 00000000..0da81199 --- /dev/null +++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor @@ -0,0 +1,34 @@ + + +
+ @this.ElementBaseTreeRowViewModel.ElementName + + + + @foreach (var categoryShortName in this.ElementBaseTreeRowViewModel.ElementBase.GetAllCategories().Select(x => x.ShortName)) + { + + + + } +
diff --git a/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor.cs b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor.cs new file mode 100644 index 00000000..fb3485f6 --- /dev/null +++ b/COMETwebapp/Components/MultiModelEditor/ElementDefinitionTreeItem.razor.cs @@ -0,0 +1,54 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2024 Starion Group S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The COMET WEB Community Edition is free software; you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// The COMET WEB Community Edition is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Components.MultiModelEditor +{ + using COMETwebapp.ViewModels.Components.MultiModelEditor.Rows; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the component + /// + public partial class ElementDefinitionTreeItem + { + /// + /// The css class string for the item + /// + [Parameter] + public string CssClass { get; set; } = string.Empty; + + /// + /// The to show in the item + /// + [Parameter] + public ElementBaseTreeRowViewModel ElementBaseTreeRowViewModel { get; set; } + + /// + /// Handle unmatched values, like "draggable" html attribute, so no error is thrown + /// + [Parameter(CaptureUnmatchedValues = true)] + public Dictionary AdditionalAttributes { get; set; } + } +} diff --git a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor index 4ad3b32d..b2d90f09 100644 --- a/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor +++ b/COMETwebapp/Components/MultiModelEditor/MultiModelEditor.razor @@ -99,14 +99,14 @@ - + - + - + - + diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs index 1c535259..f8b00b05 100644 --- a/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs +++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/ElementDefinitionTreeViewModel.cs @@ -126,11 +126,6 @@ public IterationData SelectedIterationData ///
public ObservableCollection Rows { get; private set; } = []; - /// - /// Represents the selected ElementDefinitionRowViewModel - /// - public ElementDefinition SelectedElementDefinition { get; set; } - /// /// Add rows related to that has been added /// diff --git a/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs b/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs index 0bbb0cd2..720aa34d 100644 --- a/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs +++ b/COMETwebapp/ViewModels/Components/MultiModelEditor/IElementDefinitionTreeViewModel.cs @@ -48,11 +48,6 @@ public interface IElementDefinitionTreeViewModel : IHaveReusableRows /// Iteration Iteration { get; set; } - /// - /// Represents the selected ElementDefinitionRowViewModel - /// - ElementDefinition SelectedElementDefinition { get; set; } - /// /// Gets the Description of the selected model and iteration ///