Skip to content

Commit

Permalink
Feat #697 From a tab, allow a user to open a new tab by only selectin…
Browse files Browse the repository at this point in the history
…g the view of interest (#705)
  • Loading branch information
joao4all authored Jul 17, 2024
1 parent ccdc22b commit e771c1d
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 14 deletions.
12 changes: 11 additions & 1 deletion COMET.Web.Common.Tests/Components/IndexComponentTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

namespace COMET.Web.Common.Tests.Components
{
using System.Collections.ObjectModel;
using System.Reflection;

using Bunit;
Expand Down Expand Up @@ -70,6 +71,7 @@ public class IndexComponentTestFixture
private TestAuthorizationContext authorization;
private SourceList<Iteration> sourceList;
private Mock<IRegistrationService> registrationService;
private readonly Guid modelId = Guid.NewGuid();

[SetUp]
public void Setup()
Expand All @@ -82,6 +84,7 @@ public void Setup()
this.serverConnectionService.Setup(x => x.ServerConfiguration).Returns(serverConfiguration);
this.sourceList = new SourceList<Iteration>();
this.sessionService.Setup(x => x.OpenIterations).Returns(this.sourceList);
this.sessionService.Setup(x => x.OpenEngineeringModels).Returns([]);

this.authenticationService = new Mock<IAuthenticationService>();

Expand Down Expand Up @@ -157,7 +160,7 @@ public void VerifyIndexPageWithRedirectionAuthorized()

var engineeringModelSetup = new EngineeringModelSetup
{
Iid = Guid.NewGuid(),
Iid = this.modelId,
IterationSetup =
{
new IterationSetup
Expand All @@ -167,6 +170,12 @@ public void VerifyIndexPageWithRedirectionAuthorized()
}
};

var engineeringModel = new EngineeringModel
{
Iid = this.modelId,
EngineeringModelSetup = engineeringModelSetup
};

var queries = new Dictionary<string, string>
{
[QueryKeys.DomainKey] = domain.Iid.ToShortGuid(),
Expand All @@ -183,6 +192,7 @@ public void VerifyIndexPageWithRedirectionAuthorized()
Assert.That(openModel.Instance.ViewModel.SelectedEngineeringModel, Is.Null);

this.sessionService.Setup(x => x.GetParticipantModels()).Returns(new List<EngineeringModelSetup> { engineeringModelSetup });
this.sessionService.Setup(x => x.OpenEngineeringModels).Returns(new ReadOnlyCollection<EngineeringModel>([engineeringModel]));

renderer = this.context.RenderComponent<IndexComponent>(parameters =>
parameters.Add(p => p.Redirect, url));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public virtual async Task<Result<Iteration>> OpenSession()
/// <param name="domainId">The <see cref="Guid" /> of the <see cref="DomainOfExpertise" /> to select</param>
public void PreSelectIteration(Guid modelId, Guid iterationId, Guid domainId)
{
this.selectedEngineeringModel = this.AvailableEngineeringModelSetups.FirstOrDefault(x => x.Iid == modelId);
this.selectedEngineeringModel = this.sessionService.OpenEngineeringModels.FirstOrDefault(x => x.Iid == modelId)?.EngineeringModelSetup;
var iterationSetup = this.SelectedEngineeringModel?.IterationSetup.Find(x => x.IterationIid == iterationId);

if (iterationSetup != null)
Expand Down
36 changes: 35 additions & 1 deletion COMETwebapp.Tests/Pages/TabsTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace COMETwebapp.Tests.Pages

using COMET.Web.Common.Model.Configuration;
using COMET.Web.Common.Services.ConfigurationService;
using COMET.Web.Common.Services.SessionManagement;
using COMET.Web.Common.Services.StringTableService;
using COMET.Web.Common.Test.Helpers;
using COMET.Web.Common.ViewModels.Components;
Expand All @@ -46,6 +47,8 @@ namespace COMETwebapp.Tests.Pages
using COMETwebapp.ViewModels.Components.EngineeringModel.Rows;
using COMETwebapp.ViewModels.Pages;

using DevExpress.Blazor;

using DynamicData;

using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -90,19 +93,24 @@ public void Setup()
IterationSetup = new IterationSetup
{
Container = new EngineeringModelSetup()
}
},
Container = new EngineeringModel()
};

var configuration = new Mock<IConfigurationService>();
configuration.Setup(x => x.ServerConfiguration).Returns(new ServerConfiguration());

var sessionService = new Mock<ISessionService>();
sessionService.Setup(x => x.GetDomainOfExpertise(It.IsAny<Iteration>())).Returns(new DomainOfExpertise());

this.context.ConfigureDevExpressBlazor();
this.context.Services.AddSingleton(this.viewModel.Object);
this.context.Services.AddSingleton(this.engineeringModelBodyViewModel.Object);
this.context.Services.AddSingleton(configuration.Object);
this.context.Services.AddSingleton(new Mock<IOpenTabViewModel>().Object);
this.context.Services.AddSingleton(new Mock<IOpenModelViewModel>().Object);
this.context.Services.AddSingleton(new Mock<IStringTableService>().Object);
this.context.Services.AddSingleton(sessionService.Object);

this.renderer = this.context.RenderComponent<Tabs>();
}
Expand Down Expand Up @@ -140,6 +148,32 @@ public async Task VerifyTabComponents()
Assert.That(this.renderer.Instance.IsOpenTabVisible, Is.True);
}

[Test]
public async Task VerifyTabCustomButton()
{
var openTabs = new SourceList<TabbedApplicationInformation>();
var tabToOpen = new TabbedApplicationInformation(this.engineeringModelBodyViewModel.Object, typeof(EngineeringModelBody), this.iteration);
openTabs.Add(tabToOpen);
this.viewModel.Setup(x => x.OpenTabs).Returns(openTabs);
this.viewModel.Setup(x => x.CurrentTab).Returns(tabToOpen);
this.renderer.Render();

var tabCustomButton = this.renderer.FindComponents<DxButton>().First(x => x.Instance.Id == "tab-custom-option-button");
await this.renderer.InvokeAsync(tabCustomButton.Instance.Click.InvokeAsync);
Assert.That(this.renderer.Instance.IsOpenTabVisible, Is.True);

var openTabComponent = this.renderer.FindComponent<OpenTab>();
await this.renderer.InvokeAsync(openTabComponent.Instance.OnCancel.Invoke);
Assert.That(this.renderer.Instance.IsOpenTabVisible, Is.False);

tabToOpen = new TabbedApplicationInformation(this.engineeringModelBodyViewModel.Object, typeof(EngineeringModelBody), null);
openTabs.ReplaceAt(0, tabToOpen);
this.renderer.Render();
tabCustomButton = this.renderer.FindComponents<DxButton>().First(x => x.Instance.Id == "tab-custom-option-button");
await this.renderer.InvokeAsync(tabCustomButton.Instance.Click.InvokeAsync);
Assert.That(this.renderer.Instance.IsOpenTabVisible, Is.False);
}

[Test]
public void VerifyTabsPage()
{
Expand Down
11 changes: 7 additions & 4 deletions COMETwebapp/Components/Tabs/OpenTab.razor
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,17 @@
Data="@(this.ViewModel.EngineeringModelSetups)"
TextFieldName="@nameof(EngineeringModelSetup.Name)"
@bind-Value="@(this.ViewModel.SelectedEngineeringModel)"
NullText="Select an Engineering Model"/>
NullText="Select an Engineering Model"
Enabled="@(this.ModelId == Guid.Empty)" />
</DxFormLayoutItem>

<DxFormLayoutItem Caption="Domain" ColSpanLg="12">
<DxComboBox Id="domain-selection"
Data="@(this.ViewModel.AvailablesDomainOfExpertises)"
TextFieldName="@nameof(DomainOfExpertise.Name)"
@bind-Value="@(this.ViewModel.SelectedDomainOfExpertise)"
NullText="Select a Domain of Expertise">
NullText="Select a Domain of Expertise"
Enabled="@(this.DomainId == Guid.Empty)">
<ItemTemplate Context="ctx">
@if (this.ViewModel.IsCurrentModelOpened)
{
Expand All @@ -79,7 +81,8 @@
TextFieldName="@nameof(IterationData.IterationName)"
@bind-Value="@(this.ViewModel.SelectedIterationSetup)"
NullText="Select an Iteration"
ReadOnly="@(this.ViewModel.IsCurrentModelOpened)"/>
ReadOnly="@(this.ViewModel.IsCurrentModelOpened)"
Enabled="@(this.IterationId == Guid.Empty)" />
</DxFormLayoutItem>
}

Expand Down Expand Up @@ -107,4 +110,4 @@
RenderStyleMode="ButtonRenderStyleMode.Outline"/>
}
</DxFormLayoutItem>
</DxFormLayout>
</DxFormLayout>
17 changes: 14 additions & 3 deletions COMETwebapp/Components/Tabs/TabComponent.razor
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,23 @@
@onclick="@(() => this.OnClick?.Invoke())">

<span>@(this.Text)</span>

@if (this.CustomOptionIcon is not null)
{
<DxButton RenderStyle="ButtonRenderStyle.None"
Click="@(() => this.OnCustomOptionIconClick?.Invoke())"
CssClass="ps-1 pe-0"
Id="tab-custom-option-button">
<DynamicComponent Type="@(this.CustomOptionIcon)"
Parameters="IconConfiguration"/>
</DxButton>
}

<DxButton RenderStyle="ButtonRenderStyle.None"
Click="@(() => this.OnIconClick?.Invoke())"
CssClass="px-1">
CssClass="px-1"
Id="tab-button">
<DynamicComponent Type="@(this.Icon)"
Parameters="IconConfiguration"/>
</DxButton>

</div>
</div>
14 changes: 13 additions & 1 deletion COMETwebapp/Components/Tabs/TabComponent.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,17 @@ public partial class TabComponent : DisposableComponent
public string Text { get; set; }

/// <summary>
/// Gets or sets the icon to be displayed
/// Gets or sets the icon to be displayed on the right side of a tab
/// </summary>
[Parameter]
public Type Icon { get; set; }

/// <summary>
/// Gets or sets the icon to be displayed as an option icon from a tab
/// </summary>
[Parameter]
public Type CustomOptionIcon { get; set; }

/// <summary>
/// Gets or sets the action to be executed when the tab is clicked
/// </summary>
Expand All @@ -57,6 +63,12 @@ public partial class TabComponent : DisposableComponent
[Parameter]
public Action OnIconClick { get; set; }

/// <summary>
/// Gets or sets the action to be executed when the custom option icon button is clicked
/// </summary>
[Parameter]
public Action OnCustomOptionIconClick { get; set; }

/// <summary>
/// Gets or sets the condition to check if this tab is the current one
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions COMETwebapp/Components/Tabs/TabsPanelComponent.razor
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
{
<TabComponent Text="@(GetTabText(tab.ObjectOfInterest))"
Icon="typeof(FeatherX)"
CustomOptionIcon="typeof(FeatherCopy)"
OnClick="@(() => this.OnTabClick.InvokeAsync((tab, this.Handler)))"
OnIconClick="@(() => this.OnRemoveTabClick.InvokeAsync(tab))"
OnCustomOptionIconClick="@(() => this.OnCreateTabForModel.InvokeAsync(tab))"
IsCurrent="@(tab == this.Handler.CurrentTab)"
@key="tab"/>
}
Expand Down
6 changes: 6 additions & 0 deletions COMETwebapp/Components/Tabs/TabsPanelComponent.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ public partial class TabsPanelComponent : DisposableComponent
[Parameter]
public Action OnOpenTabClick { get; set; }

/// <summary>
/// Gets or sets the method to be executed when the open new tab for the selected model button is clicked
/// </summary>
[Parameter]
public EventCallback<TabbedApplicationInformation> OnCreateTabForModel { get; set; }

/// <summary>
/// Gets or sets the method to be executed when the remove tab button is clicked
/// </summary>
Expand Down
9 changes: 7 additions & 2 deletions COMETwebapp/Pages/Tabs.razor
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<TabsPanelComponent Handler="@(this.ViewModel)"
ViewModel="@(this.ViewModel)"
OnTabClick="@(tuple => OnTabClick(tuple.Item1, tuple.Item2))"
OnCreateTabForModel="@(tab => this.OnCreateTabForModel(tab))"
OnRemoveTabClick="@(tab => this.OnRemoveTabClick(tab))"
OnOpenTabClick="@(() => this.OnOpenTabClick())"
Tabs="@(this.OpenTabsFromSelectedApplication.Where(x => x.Panel == null).ToList())"
Expand Down Expand Up @@ -60,8 +61,12 @@
ShowHeader="false"
ShowCloseButton="true"
CloseOnOutsideClick="true"
CloseOnEscape="true">
CloseOnEscape="true"
Closed="@(() => this.ResetOpenTabPopup())">
<OpenTab OnCancel="@(() => this.SetOpenTabVisibility(false))"
OnTabOpened="@(() => this.SetOpenTabVisibility(false))"
Panel="@(this.SelectedSidePanel)"/>
Panel="@(this.SelectedSidePanel)"
ModelId="this.ModelId"
IterationId="this.IterationId"
DomainId="this.DomainId"/>
</DxPopup>
60 changes: 59 additions & 1 deletion COMETwebapp/Pages/Tabs.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@

namespace COMETwebapp.Pages
{
using CDP4Common.EngineeringModelData;

using COMET.Web.Common.Extensions;
using COMET.Web.Common.Services.SessionManagement;

using COMETwebapp.Model;
using COMETwebapp.ViewModels.Pages;
Expand All @@ -50,12 +53,33 @@ public partial class Tabs
/// </summary>
private IEnumerable<TabbedApplicationInformation> OpenTabsFromSelectedApplication => this.ViewModel.OpenTabs.Items.Where(x => x.ComponentType == this.ViewModel.SelectedApplication?.ComponentType);

/// <summary>
/// The model id to fill the opentab form, if needed
/// </summary>
private Guid ModelId { get; set; }

/// <summary>
/// The iteration id to fill the opentab form, if needed
/// </summary>
private Guid IterationId { get; set; }

/// <summary>
/// The domain id to fill the opentab form, if needed
/// </summary>
private Guid DomainId { get; set; }

/// <summary>
/// Gets or sets the injected <see cref="ITabsViewModel" />
/// </summary>
[Inject]
public ITabsViewModel ViewModel { get; set; }

/// <summary>
/// Gets or sets the injected <see cref="ISessionService" />
/// </summary>
[Inject]
public ISessionService SessionService { get; set; }

/// <summary>
/// Gets the open tab component visibility
/// </summary>
Expand Down Expand Up @@ -113,7 +137,41 @@ private void SetOpenTabVisibility(bool visibility)
private void OnOpenTabClick(TabPanelInformation sidePanel = null)
{
this.SelectedSidePanel = sidePanel;
this.SetOpenTabVisibility(true);
this.SetOpenTabVisibility(true);
}

/// <summary>
/// Resets the preset id's used to fill the open tab form
/// </summary>
private void ResetOpenTabPopup()
{
this.DomainId = Guid.Empty;
this.IterationId = Guid.Empty;
this.ModelId = Guid.Empty;
}

/// <summary>
/// Method executed when the button to open a new view of the selected model is clicked
/// </summary>
/// <param name="tabbedApplicationInformation">The selected tab that contains the model of interest</param>
private void OnCreateTabForModel(TabbedApplicationInformation tabbedApplicationInformation)
{
var iterationOfInterest = tabbedApplicationInformation.ObjectOfInterest switch
{
Iteration iteration => iteration,
CDP4Common.EngineeringModelData.EngineeringModel model => model.Iteration.First(x => x.IterationSetup.FrozenOn == null),
_ => null
};

if (iterationOfInterest == null)
{
return;
}

this.IterationId = iterationOfInterest.Iid;
this.ModelId = ((CDP4Common.EngineeringModelData.EngineeringModel)iterationOfInterest.Container).Iid;
this.DomainId = this.SessionService.GetDomainOfExpertise(iterationOfInterest).Iid;
this.SetOpenTabVisibility(true);
}
}
}

0 comments on commit e771c1d

Please sign in to comment.