From e528d3bc4cb4b8b19edfb6454c0cbbe037c788ed Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Fri, 22 Sep 2023 17:30:04 +0200 Subject: [PATCH 01/10] New UI for the InputEditor and add MultiCombobox --- COMET.Web.Common/COMET.Web.Common.csproj | 3 + .../Components/BookEditor/InputEditor.razor | 112 ++++++++---------- .../BookEditor/InputEditor.razor.cs | 78 +++++++++++- .../Components/MultiComboBox.razor | 73 ++++++++++++ .../Components/MultiComboBox.razor.cs | 78 ++++++++++++ .../Components/MultiComboBox.razor.css | 31 +++++ .../wwwroot/BookInputConfiguration.json | 4 + COMET.Web.Common/wwwroot/css/styles.css | 45 ++++++- 8 files changed, 359 insertions(+), 65 deletions(-) create mode 100644 COMET.Web.Common/Components/MultiComboBox.razor create mode 100644 COMET.Web.Common/Components/MultiComboBox.razor.cs create mode 100644 COMET.Web.Common/Components/MultiComboBox.razor.css create mode 100644 COMET.Web.Common/wwwroot/BookInputConfiguration.json diff --git a/COMET.Web.Common/COMET.Web.Common.csproj b/COMET.Web.Common/COMET.Web.Common.csproj index 246988fb..7890b639 100644 --- a/COMET.Web.Common/COMET.Web.Common.csproj +++ b/COMET.Web.Common/COMET.Web.Common.csproj @@ -47,6 +47,9 @@ Always + + Always + Always diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor b/COMET.Web.Common/Components/BookEditor/InputEditor.razor index 1d83734f..0f57520c 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor @@ -27,67 +27,59 @@ @using CDP4Common.ReportingData @typeparam TItem -
- - -
- @if (this.Item is INamedThing namedThing) - { -
-

Name:

- -
- } +
+ @if (this.Item is INamedThing namedThing && this.showName) + { +
+

Name:

+ +
+ } - @if (this.Item is IShortNamedThing shortNamedThing) - { -
-

ShortName:

- -
- } + @if (this.Item is IShortNamedThing shortNamedThing && this.showShortName) + { +
+

ShortName:

+ +
+ } - @if (this.Item is IOwnedThing ownedThing) - { -
-

Owner:

- -
- } + @if (this.Item is IOwnedThing ownedThing) + { +
+

Owner:

+ +
+ } - @if (this.Item is TextualNote textualNote) - { -
-

Content:

- -
- } -
- - -
- @if (this.Item is ICategorizableThing categorizableThing) - { - - - } -
-
- + @if (this.Item is TextualNote textualNote) + { +
+

Content:

+ +
+ } + + @if (this.Item is ICategorizableThing categorizableThing) + { +
+

Category:

+ + + @context.Name + + +
+ }
diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs index 0f6c2ac5..a4aa9133 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs @@ -24,17 +24,42 @@ // -------------------------------------------------------------------------------------------------------------------- namespace COMET.Web.Common.Components.BookEditor -{ - using CDP4Common.EngineeringModelData; +{ + using System.Text.Json; + + using CDP4Common.EngineeringModelData; using CDP4Common.SiteDirectoryData; + using COMET.Web.Common.Model; + using COMET.Web.Common.Utilities; + using Microsoft.AspNetCore.Components; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; /// /// Support class for the InputEditor component /// public partial class InputEditor { + /// + /// Gets or sets the + /// + [Inject] + ILogger> Logger { get; set; } + + /// + /// Gets or sets the + /// + [Inject] + HttpClient HttpClient { get; set; } + + /// + /// Gets or sets the + /// + [Inject] + public IOptions Options { get; set; } + /// /// Gets or sets the item for which the input is being provided /// @@ -52,7 +77,54 @@ public partial class InputEditor /// [Parameter] public IEnumerable AvailableCategories { get; set; } - + + /// + /// Sets if the component should show the name field + /// + private bool showName; + + /// + /// Sets if the component should show the shorname field + /// + private bool showShortName; + + /// + /// Method invoked when the component is ready to start, having received its + /// initial parameters from its parent in the render tree. + /// + /// Override this method if you will perform an asynchronous operation and + /// want the component to refresh when that operation is completed. + /// + /// A representing any asynchronous operation. + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + + var jsonFile = this.Options.Value.JsonConfigurationFile ?? "BookInputConfiguration.json"; + + try + { + var path = ContentPathBuilder.BuildPath(jsonFile); + var jsonContent = await this.HttpClient.GetStreamAsync(path); + var configurations = JsonSerializer.Deserialize>(jsonContent); + + if (configurations.TryGetValue("ShowName", out var showNameValue)) + { + this.showName = showNameValue; + } + + if (configurations.TryGetValue("ShowShortName", out var showShortNameValue)) + { + this.showShortName = showShortNameValue; + } + } + catch (Exception e) + { + this.Logger.LogError(e, "Error while getting the configuration file."); + return; + } + } + /// /// Handler for when the selected categories changed /// diff --git a/COMET.Web.Common/Components/MultiComboBox.razor b/COMET.Web.Common/Components/MultiComboBox.razor new file mode 100644 index 00000000..29d4d824 --- /dev/null +++ b/COMET.Web.Common/Components/MultiComboBox.razor @@ -0,0 +1,73 @@ + +@typeparam TItem + + + +
+ @if (this.ShowCheckBoxes) + { + var isSelected = this.Values.Contains(context); + + } + + @if (this.RowTemplate != null) + { + @this.RowTemplate(context) + } + else + { + @context.ToString() + } +
+
+ + + @if(this.Values.Count <= 2) + { +
+ @foreach(var value in this.Values) + { + + } +
+ } + else + { +
@(this.Values.Count) items are selected
+ } +
+
\ No newline at end of file diff --git a/COMET.Web.Common/Components/MultiComboBox.razor.cs b/COMET.Web.Common/Components/MultiComboBox.razor.cs new file mode 100644 index 00000000..5b904a2f --- /dev/null +++ b/COMET.Web.Common/Components/MultiComboBox.razor.cs @@ -0,0 +1,78 @@ +// ----------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Jaime Bernar +// +// This file is part of AIDA +// European Space Agency Community License – v2.4 Permissive (Type 3) +// See LICENSE file for details +// +// +// ----------------------------------------------------------------------------------- + +namespace COMET.Web.Common.Components +{ + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the multicombobox component + /// + public partial class MultiComboBox + { + /// + /// Gets or sets if the checkboxes for the selected items should be drawn + /// + [Parameter] + public bool ShowCheckBoxes { get; set; } + + /// + /// Gets or sets the item template for the selected items + /// + [Parameter] + public RenderFragment RowTemplate { get; set; } + + /// + /// Gets or sets the data of the combobox + /// + [Parameter] + public IEnumerable Data { get; set; } = Enumerable.Empty(); + + /// + /// Gets or sets if the component should show all the fields as readonly + /// + [Parameter] + public bool Enabled { get; set; } = true; + + /// + /// Gets or sets the value of the selected items + /// + [Parameter] + public List Values { get; set; } = new(); + + /// + /// Gets or sets the callback used to update the component value + /// + [Parameter] + public EventCallback> ValuesChanged { get; set; } + + /// + /// Handler for when the value of the component has changed + /// + /// the new value + /// an asynchronous operation + private async Task ItemSelected(TItem newValue) + { + if(this.Values.Contains(newValue)) + { + this.Values.Remove(newValue); + } + else + { + this.Values.Add(newValue); + } + + await this.ValuesChanged.InvokeAsync(this.Values); + } + } +} diff --git a/COMET.Web.Common/Components/MultiComboBox.razor.css b/COMET.Web.Common/Components/MultiComboBox.razor.css new file mode 100644 index 00000000..9ad30922 --- /dev/null +++ b/COMET.Web.Common/Components/MultiComboBox.razor.css @@ -0,0 +1,31 @@ +::deep.chip { + border: 0; + background-color: #EEEEEE; + border-radius: 15px; + padding: 2.5% 5%; + box-shadow: 0px 0px 0px 1px rgba(0,0,0,0.2); +} + +::deep.chips-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + row-gap: 5px; + column-gap: 5px; +} + +::deep.dxbl-listbox-item{ + padding: 0 !important; +} + +::deep.combo-box-item-template { + display: block; + height: 100%; + width: 100%; + padding: 2%; +} + +::deep.combo-box-item-template.selected { + background-color: #0d6efd; + color: white; +} \ No newline at end of file diff --git a/COMET.Web.Common/wwwroot/BookInputConfiguration.json b/COMET.Web.Common/wwwroot/BookInputConfiguration.json new file mode 100644 index 00000000..a0c6a76d --- /dev/null +++ b/COMET.Web.Common/wwwroot/BookInputConfiguration.json @@ -0,0 +1,4 @@ +{ + "ShowName": true, + "ShowShortName" : true +} diff --git a/COMET.Web.Common/wwwroot/css/styles.css b/COMET.Web.Common/wwwroot/css/styles.css index a730d676..ee7b7cc8 100644 --- a/COMET.Web.Common/wwwroot/css/styles.css +++ b/COMET.Web.Common/wwwroot/css/styles.css @@ -500,7 +500,48 @@ color: #16386f; } -.tab-content { +/**MULTICOMBOBOX COMPONENT*/ +::deep.chip { + border: 0; + background-color: #EEEEEE; + border-radius: 15px; + padding: 2.5% 5%; + box-shadow: 0px 0px 0px 1px rgba(0,0,0,0.2); +} + +::deep.chips-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + row-gap: 5px; + column-gap: 5px; +} + +::deep.dxbl-listbox-item { + padding: 0 !important; +} + +::deep.combo-box-item-template { + display: block; + height: 100%; + width: 100%; + padding: 2%; +} + + ::deep.combo-box-item-template.selected { + background-color: #0d6efd; + color: white; + } + +.multi-combo-item-template { + display: flex; + align-items:center; + column-gap: 10px; +} + +/*BOOK EDITOR*/ + +.input-content { display: flex; flex-direction: column; align-items: start; @@ -518,4 +559,4 @@ .editor-row > p { width: 25%; margin: 0; - } \ No newline at end of file + } From e3a0adf3c2722aeb1fc996c5688cefe8b91ffb71 Mon Sep 17 00:00:00 2001 From: jaimeatrhea Date: Tue, 26 Sep 2023 12:34:55 +0200 Subject: [PATCH 02/10] Add improvements to the MultiComboBox component --- .../Components/BookEditor/InputEditor.razor | 1 - .../BookEditor/InputEditor.razor.cs | 20 ++++++++++++++---- .../Components/MultiComboBox.razor | 18 ++++++++++++---- .../Components/MultiComboBox.razor.cs | 21 ++++++++++++++++++- .../Components/MultiComboBox.razor.css | 20 ++++++++++++++++-- .../wwwroot/css/SVG/icons/close.svg | 1 + COMET.Web.Common/wwwroot/css/styles.css | 4 ++++ 7 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 COMET.Web.Common/wwwroot/css/SVG/icons/close.svg diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor b/COMET.Web.Common/Components/BookEditor/InputEditor.razor index 0f57520c..37eda25a 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor @@ -74,7 +74,6 @@ @context.Name diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs index a4aa9133..35cd9c7b 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs @@ -31,6 +31,7 @@ namespace COMET.Web.Common.Components.BookEditor using CDP4Common.SiteDirectoryData; using COMET.Web.Common.Model; + using COMET.Web.Common.Services.SessionManagement; using COMET.Web.Common.Utilities; using Microsoft.AspNetCore.Components; @@ -46,20 +47,26 @@ public partial class InputEditor /// Gets or sets the /// [Inject] - ILogger> Logger { get; set; } + public ILogger> Logger { get; set; } /// /// Gets or sets the /// [Inject] - HttpClient HttpClient { get; set; } + public HttpClient HttpClient { get; set; } - /// - /// Gets or sets the + /// + /// Gets or sets the /// [Inject] public IOptions Options { get; set; } + /// + /// Gets or sets the + /// + [Inject] + public ISessionService SessionService { get; set; } + /// /// Gets or sets the item for which the input is being provided /// @@ -117,6 +124,11 @@ protected override async Task OnInitializedAsync() { this.showShortName = showShortNameValue; } + + if (this.Item is IOwnedThing ownedThing) + { + ownedThing.Owner = this.SessionService.Session.ActivePerson.DefaultDomain; + } } catch (Exception e) { diff --git a/COMET.Web.Common/Components/MultiComboBox.razor b/COMET.Web.Common/Components/MultiComboBox.razor index 29d4d824..af62e64f 100644 --- a/COMET.Web.Common/Components/MultiComboBox.razor +++ b/COMET.Web.Common/Components/MultiComboBox.razor @@ -26,6 +26,8 @@ Data="@this.Data" ValueChanged="@this.ItemSelected" Enabled="@this.Enabled" + Value="@this.lastSelectedValue" + ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto" CssClass="w-100">
@@ -47,12 +49,12 @@ - @if(this.Values.Count <= 2) + @if(this.Values.Count <= this.MaxNumberOfChips) {
@foreach(var value in this.Values) { - + +
}
} else { -
@(this.Values.Count) items are selected
+ if (this.EditorTextTemplate != null) + { + @this.EditorTextTemplate + } + else + { +
@(this.Values.Count) items are selected
+ } } \ No newline at end of file diff --git a/COMET.Web.Common/Components/MultiComboBox.razor.cs b/COMET.Web.Common/Components/MultiComboBox.razor.cs index 5b904a2f..cc06144c 100644 --- a/COMET.Web.Common/Components/MultiComboBox.razor.cs +++ b/COMET.Web.Common/Components/MultiComboBox.razor.cs @@ -20,11 +20,22 @@ namespace COMET.Web.Common.Components ///
public partial class MultiComboBox { + /// + /// The last selected value of the combobox + /// + private TItem lastSelectedValue; + + /// + /// Gets or sets the maximum number of chips that the combo should show + /// + [Parameter] + public int MaxNumberOfChips { get; set; } = 3; + /// /// Gets or sets if the checkboxes for the selected items should be drawn /// [Parameter] - public bool ShowCheckBoxes { get; set; } + public bool ShowCheckBoxes { get; set; } = true; /// /// Gets or sets the item template for the selected items @@ -32,6 +43,12 @@ public partial class MultiComboBox [Parameter] public RenderFragment RowTemplate { get; set; } + /// + /// Gets or sets item template to show when the number of selected items is greater or equal to the + /// + [Parameter] + public RenderFragment EditorTextTemplate { get; set; } + /// /// Gets or sets the data of the combobox /// @@ -63,6 +80,8 @@ public partial class MultiComboBox /// an asynchronous operation private async Task ItemSelected(TItem newValue) { + this.lastSelectedValue = default; + if(this.Values.Contains(newValue)) { this.Values.Remove(newValue); diff --git a/COMET.Web.Common/Components/MultiComboBox.razor.css b/COMET.Web.Common/Components/MultiComboBox.razor.css index 9ad30922..bf7ef31b 100644 --- a/COMET.Web.Common/Components/MultiComboBox.razor.css +++ b/COMET.Web.Common/Components/MultiComboBox.razor.css @@ -1,17 +1,33 @@ ::deep.chip { border: 0; background-color: #EEEEEE; - border-radius: 15px; - padding: 2.5% 5%; + border-radius: 30px; + padding: 2%; box-shadow: 0px 0px 0px 1px rgba(0,0,0,0.2); + color: #555; + display: flex; + align-items:center; + justify-content:center; + column-gap: 5px; } +::deep.chip-button { + border: 0; + border-radius: 30px; +} + + ::deep.chip-button:hover { + box-shadow: 0px 0px 0px 2px rgba(0,0,0,0.2); + } + ::deep.chips-container { display: flex; flex-direction: row; flex-wrap: wrap; row-gap: 5px; column-gap: 5px; + width: 100%; + height: 100%; } ::deep.dxbl-listbox-item{ diff --git a/COMET.Web.Common/wwwroot/css/SVG/icons/close.svg b/COMET.Web.Common/wwwroot/css/SVG/icons/close.svg new file mode 100644 index 00000000..3c597777 --- /dev/null +++ b/COMET.Web.Common/wwwroot/css/SVG/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/COMET.Web.Common/wwwroot/css/styles.css b/COMET.Web.Common/wwwroot/css/styles.css index ee7b7cc8..d05981b9 100644 --- a/COMET.Web.Common/wwwroot/css/styles.css +++ b/COMET.Web.Common/wwwroot/css/styles.css @@ -60,6 +60,10 @@ background-image: url(SVG/icons/trash.svg) } +.icon-close { + background-image: url(SVG/icons/close.svg) +} + .logo-size { max-width: 95px; height: auto; From aeed121cb71f0a5ff15dd438d310e1a98734d637 Mon Sep 17 00:00:00 2001 From: Robbware Date: Tue, 3 Oct 2023 12:30:48 +0100 Subject: [PATCH 03/10] Use constants for the property names on the configuration files for BookInputConfiguration --- .gitignore | 3 +++ .../BookEditor/InputEditor.razor.cs | 23 +++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 555c6309..c2732183 100644 --- a/.gitignore +++ b/.gitignore @@ -349,3 +349,6 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ /COMETwebapp/Tools + +# Jetbrains folders +.idea/ \ No newline at end of file diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs index 35cd9c7b..73c6435b 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs @@ -85,16 +85,26 @@ public partial class InputEditor [Parameter] public IEnumerable AvailableCategories { get; set; } - /// - /// Sets if the component should show the name field + /// + /// Sets if the component should show the name field /// private bool showName; - /// - /// Sets if the component should show the shorname field + /// + /// The name of the ShowName property on the configuration file + /// + private const string showNameConfigurationProperty = "ShowName"; + + /// + /// Sets if the component should show the shorname field /// private bool showShortName; + /// + /// The name of the ShowShortName property on the configuration file + /// + private const string showShortNameConfigurationProperty = "ShowShortName"; + /// /// Method invoked when the component is ready to start, having received its /// initial parameters from its parent in the render tree. @@ -115,12 +125,12 @@ protected override async Task OnInitializedAsync() var jsonContent = await this.HttpClient.GetStreamAsync(path); var configurations = JsonSerializer.Deserialize>(jsonContent); - if (configurations.TryGetValue("ShowName", out var showNameValue)) + if (configurations.TryGetValue(showNameConfigurationProperty, out var showNameValue)) { this.showName = showNameValue; } - if (configurations.TryGetValue("ShowShortName", out var showShortNameValue)) + if (configurations.TryGetValue(showShortNameConfigurationProperty, out var showShortNameValue)) { this.showShortName = showShortNameValue; } @@ -133,7 +143,6 @@ protected override async Task OnInitializedAsync() catch (Exception e) { this.Logger.LogError(e, "Error while getting the configuration file."); - return; } } From 9f22892e246a1c8170f24ecad67fdd97e43acffa Mon Sep 17 00:00:00 2001 From: Robbware Date: Tue, 3 Oct 2023 15:29:02 +0100 Subject: [PATCH 04/10] Parsing code review comments and fixing InputEditor and EditorPopup text fixtures to match the new business rules and changes made. --- .gitignore | 2 +- .../BookEditor/EditotPopupTestFixture.cs | 5 ++ .../BookEditor/InputEditorTestFixture.cs | 60 ++++++++++++------- .../BookEditor/InputEditor.razor.cs | 33 ++++++---- .../Components/MultiComboBox.razor.cs | 30 +++++++--- .../Components/MultiComboBox.razor.css | 47 --------------- 6 files changed, 88 insertions(+), 89 deletions(-) delete mode 100644 COMET.Web.Common/Components/MultiComboBox.razor.css diff --git a/.gitignore b/.gitignore index c2732183..d93cc446 100644 --- a/.gitignore +++ b/.gitignore @@ -351,4 +351,4 @@ MigrationBackup/ /COMETwebapp/Tools # Jetbrains folders -.idea/ \ No newline at end of file +.idea/ diff --git a/COMET.Web.Common.Tests/Components/BookEditor/EditotPopupTestFixture.cs b/COMET.Web.Common.Tests/Components/BookEditor/EditotPopupTestFixture.cs index 81cb95cb..a0ff55e9 100644 --- a/COMET.Web.Common.Tests/Components/BookEditor/EditotPopupTestFixture.cs +++ b/COMET.Web.Common.Tests/Components/BookEditor/EditotPopupTestFixture.cs @@ -31,6 +31,7 @@ namespace COMET.Web.Common.Tests.Components.BookEditor using COMET.Web.Common.Components; using COMET.Web.Common.Components.BookEditor; + using COMET.Web.Common.Services.SessionManagement; using COMET.Web.Common.Test.Helpers; using COMET.Web.Common.ViewModels.Components.BookEditor; @@ -40,6 +41,7 @@ namespace COMET.Web.Common.Tests.Components.BookEditor using DynamicData; using Microsoft.AspNetCore.Components; + using Microsoft.Extensions.DependencyInjection; using Moq; @@ -58,12 +60,15 @@ public class EditotPopupTestFixture private List availableCategories; private bool onCancelCalled; private bool onAcceptCalled; + private Mock sessionService; [SetUp] public void Setup() { this.context = new TestContext(); this.context.ConfigureDevExpressBlazor(); + this.sessionService = new Mock(); + this.context.Services.AddSingleton(this.sessionService.Object); this.book = new Book(); diff --git a/COMET.Web.Common.Tests/Components/BookEditor/InputEditorTestFixture.cs b/COMET.Web.Common.Tests/Components/BookEditor/InputEditorTestFixture.cs index fab129e2..bd9de519 100644 --- a/COMET.Web.Common.Tests/Components/BookEditor/InputEditorTestFixture.cs +++ b/COMET.Web.Common.Tests/Components/BookEditor/InputEditorTestFixture.cs @@ -24,18 +24,28 @@ namespace COMET.Web.Common.Tests.Components.BookEditor { + using System.Text.Json; + using Bunit; using CDP4Common.ReportingData; using CDP4Common.SiteDirectoryData; + using COMET.Web.Common.Components; using COMET.Web.Common.Components.BookEditor; + using COMET.Web.Common.Services.SessionManagement; using COMET.Web.Common.Test.Helpers; using DevExpress.Blazor; - + + using Microsoft.Extensions.DependencyInjection; + + using Moq; + using NUnit.Framework; + using RichardSzalay.MockHttp; + using TestContext = Bunit.TestContext; [TestFixture] @@ -46,12 +56,26 @@ public class InputEditorTestFixture private Book book; private List activeDomains; private List availableCategories; - + private Mock sessionService; + private MockHttpMessageHandler mockHttpMessageHandler; + private HttpClient httpClient; + private const string BookName = "Book Example"; + private const string BookShortName = "bookExample"; + [SetUp] public void Setup() { this.context = new TestContext(); this.context.ConfigureDevExpressBlazor(); + this.sessionService = new Mock(); + this.context.Services.AddSingleton(this.sessionService.Object); + this.mockHttpMessageHandler = new MockHttpMessageHandler(); + this.httpClient = this.mockHttpMessageHandler.ToHttpClient(); + this.httpClient.BaseAddress = new Uri("http://localhost/"); + this.context.Services.AddScoped(_ => this.httpClient); + var httpResponse = new HttpResponseMessage(); + httpResponse.Content = new StringContent("{\n \"ShowName\": true,\n \"ShowShortName\" : true \n}\n"); + this.mockHttpMessageHandler.When(HttpMethod.Get, "/_content/CDP4.WEB.Common/BookInputConfiguration.json").Respond(_ => httpResponse); this.activeDomains = new List { @@ -65,8 +89,8 @@ public void Setup() this.book = new Book() { - Name = "Book Example", - ShortName = "bookExample", + Name = BookName, + ShortName = BookShortName, Owner = this.activeDomains.First(), Category = this.availableCategories }; @@ -82,33 +106,27 @@ public void Setup() [Test] public void VerifyComponent() { - var basicTab = this.component.Find(".basic-tab"); - basicTab.Click(); - var textboxes = this.component.FindComponents(); var combobox = this.component.FindComponent>(); + var categoryComboBox = this.component.FindComponent>(); - var nameTextbox = textboxes[0]; - var shortNameTextbox = textboxes[1]; - + var nameTextbox = textboxes.FirstOrDefault(x => x.Instance.Text == BookName); + var shortNameTextbox = textboxes.FirstOrDefault(x => x.Instance.Text == BookShortName); + Assert.Multiple(() => { - Assert.That(nameTextbox.Instance.Text, Is.EqualTo("Book Example")); - Assert.That(shortNameTextbox.Instance.Text, Is.EqualTo("bookExample")); + Assert.IsNotNull(nameTextbox); + Assert.IsNotNull(shortNameTextbox); Assert.That(combobox.Instance.Value, Is.EqualTo(this.activeDomains.First())); + Assert.That(categoryComboBox.Instance, Is.Not.Null); }); - - var categoryTab = this.component.Find(".category-tab"); - categoryTab.Click(); - + this.component.Render(); - - var listbox = this.component.FindComponent>(); - + Assert.Multiple(() => { - Assert.That(listbox.Instance.Data, Is.EquivalentTo(this.availableCategories)); - Assert.That(listbox.Instance.Values, Is.EquivalentTo(this.availableCategories)); + Assert.That(categoryComboBox.Instance.Data, Is.EquivalentTo(this.availableCategories)); + Assert.That(categoryComboBox.Instance.Values, Is.EquivalentTo(this.availableCategories)); }); } } diff --git a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs index 73c6435b..4021b281 100644 --- a/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs +++ b/COMET.Web.Common/Components/BookEditor/InputEditor.razor.cs @@ -43,14 +43,14 @@ namespace COMET.Web.Common.Components.BookEditor /// public partial class InputEditor { - /// - /// Gets or sets the + /// + /// Gets or sets the /// [Inject] public ILogger> Logger { get; set; } - /// - /// Gets or sets the + /// + /// Gets or sets the /// [Inject] public HttpClient HttpClient { get; set; } @@ -61,8 +61,8 @@ public partial class InputEditor [Inject] public IOptions Options { get; set; } - /// - /// Gets or sets the + /// + /// Gets or sets the /// [Inject] public ISessionService SessionService { get; set; } @@ -73,8 +73,8 @@ public partial class InputEditor [Parameter] public TItem Item { get; set; } - /// - /// Gets or sets the active + /// + /// Gets or sets the active /// [Parameter] public IEnumerable ActiveDomains { get; set; } @@ -121,9 +121,7 @@ protected override async Task OnInitializedAsync() try { - var path = ContentPathBuilder.BuildPath(jsonFile); - var jsonContent = await this.HttpClient.GetStreamAsync(path); - var configurations = JsonSerializer.Deserialize>(jsonContent); + var configurations = await this.GetBookInputConfigurationAsync(jsonFile); if (configurations.TryGetValue(showNameConfigurationProperty, out var showNameValue)) { @@ -146,6 +144,19 @@ protected override async Task OnInitializedAsync() } } + /// + /// Acquires the BookInput configurations + /// + /// The file name that contains the configurations + /// A KeyValuePair collection with each available configuration + private async Task> GetBookInputConfigurationAsync(string fileName) + { + var path = ContentPathBuilder.BuildPath(fileName); + var jsonContent = await this.HttpClient.GetStreamAsync(path); + var configurations = JsonSerializer.Deserialize>(jsonContent); + return configurations; + } + /// /// Handler for when the selected categories changed /// diff --git a/COMET.Web.Common/Components/MultiComboBox.razor.cs b/COMET.Web.Common/Components/MultiComboBox.razor.cs index cc06144c..2466e144 100644 --- a/COMET.Web.Common/Components/MultiComboBox.razor.cs +++ b/COMET.Web.Common/Components/MultiComboBox.razor.cs @@ -1,15 +1,27 @@ -// ----------------------------------------------------------------------------------- -// -// Copyright (c) 2023 RHEA System S.A. +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. // -// Authors: Sam Gerené, Jaime Bernar +// Authors: Sam Gerené, Jaime Bernar // -// This file is part of AIDA -// European Space Agency Community License – v2.4 Permissive (Type 3) -// See LICENSE file for details +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 +// Annex A and Annex C. // -// -// ----------------------------------------------------------------------------------- +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// -------------------------------------------------------------------------------------------------------------------- namespace COMET.Web.Common.Components { diff --git a/COMET.Web.Common/Components/MultiComboBox.razor.css b/COMET.Web.Common/Components/MultiComboBox.razor.css deleted file mode 100644 index bf7ef31b..00000000 --- a/COMET.Web.Common/Components/MultiComboBox.razor.css +++ /dev/null @@ -1,47 +0,0 @@ -::deep.chip { - border: 0; - background-color: #EEEEEE; - border-radius: 30px; - padding: 2%; - box-shadow: 0px 0px 0px 1px rgba(0,0,0,0.2); - color: #555; - display: flex; - align-items:center; - justify-content:center; - column-gap: 5px; -} - -::deep.chip-button { - border: 0; - border-radius: 30px; -} - - ::deep.chip-button:hover { - box-shadow: 0px 0px 0px 2px rgba(0,0,0,0.2); - } - -::deep.chips-container { - display: flex; - flex-direction: row; - flex-wrap: wrap; - row-gap: 5px; - column-gap: 5px; - width: 100%; - height: 100%; -} - -::deep.dxbl-listbox-item{ - padding: 0 !important; -} - -::deep.combo-box-item-template { - display: block; - height: 100%; - width: 100%; - padding: 2%; -} - -::deep.combo-box-item-template.selected { - background-color: #0d6efd; - color: white; -} \ No newline at end of file From 05cf9a8305b56842379154a485bcb8e53bd58c08 Mon Sep 17 00:00:00 2001 From: Robbware Date: Tue, 3 Oct 2023 16:28:54 +0100 Subject: [PATCH 05/10] Add basic coverage for the MultiComboBox component. --- .../Components/MultiComboBoxTestFixture.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs diff --git a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs new file mode 100644 index 00000000..a5c92ba9 --- /dev/null +++ b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs @@ -0,0 +1,90 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine +// +// This file is part of COMET WEB Community Edition +// The COMET WEB Community Edition is the RHEA 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 COMET.Web.Common.Tests.Components; + +using Bunit; + +using CDP4Common.DTO; + +using COMET.Web.Common.Components; +using COMET.Web.Common.Test.Helpers; + +using DevExpress.Blazor; + +using Microsoft.AspNetCore.Authorization.Infrastructure; + +using TestContext = Bunit.TestContext; + +using NUnit.Framework; + +[TestFixture] +public class MultiComboBoxTestFixture +{ + private TestContext context; + private IRenderedComponent> component; + private List availableCategories; + + [SetUp] + public void SetUp() + { + this.context = new TestContext(); + this.context.ConfigureDevExpressBlazor(); + + this.availableCategories = new List + { + new() { Name = "Category" } + }; + + this.component = this.context.RenderComponent>(parameter => + { + parameter.Add(p => p.Data, this.availableCategories); + parameter.Add(p => p.Values, this.availableCategories); + parameter.Add(p => p.ShowCheckBoxes, true); + parameter.Add(p => p.MaxNumberOfChips, 2); + parameter.Add(p => p.Enabled, true); + }); + } + + [Test] + public void VerifyComponent() + { + Assert.Multiple(() => + { + Assert.IsTrue(this.component.Instance.Enabled); + Assert.IsNotEmpty(this.component.Instance.Data); + Assert.IsNotEmpty(this.component.Instance.Values); + Assert.IsTrue(this.component.Instance.ShowCheckBoxes); + }); + + this.component.Render(); + + var comboBox = this.component.FindComponent>(); + + Assert.Multiple(() => + { + Assert.IsNotNull(comboBox); + }); + } +} From 27d722b5065d9d9db68be049a4327c6e9cea5d3e Mon Sep 17 00:00:00 2001 From: Robbware Date: Tue, 3 Oct 2023 16:31:10 +0100 Subject: [PATCH 06/10] Refactor the MultiComboBoxTextFixture to ensure the namespace follows the project's convention. --- .../Components/MultiComboBoxTestFixture.cs | 100 +++++++++--------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs index a5c92ba9..d0344af6 100644 --- a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs +++ b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs @@ -22,69 +22,67 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace COMET.Web.Common.Tests.Components; - -using Bunit; +namespace COMET.Web.Common.Tests.Components +{ + using Bunit; -using CDP4Common.DTO; + using CDP4Common.DTO; -using COMET.Web.Common.Components; -using COMET.Web.Common.Test.Helpers; + using COMET.Web.Common.Components; + using COMET.Web.Common.Test.Helpers; -using DevExpress.Blazor; + using DevExpress.Blazor; -using Microsoft.AspNetCore.Authorization.Infrastructure; + using Microsoft.AspNetCore.Authorization.Infrastructure; -using TestContext = Bunit.TestContext; + using TestContext = Bunit.TestContext; -using NUnit.Framework; + using NUnit.Framework; -[TestFixture] -public class MultiComboBoxTestFixture -{ - private TestContext context; - private IRenderedComponent> component; - private List availableCategories; - - [SetUp] - public void SetUp() + [TestFixture] + public class MultiComboBoxTestFixture { - this.context = new TestContext(); - this.context.ConfigureDevExpressBlazor(); - - this.availableCategories = new List - { - new() { Name = "Category" } - }; + private TestContext context; + private IRenderedComponent> component; + private List availableCategories; - this.component = this.context.RenderComponent>(parameter => + [SetUp] + public void SetUp() { - parameter.Add(p => p.Data, this.availableCategories); - parameter.Add(p => p.Values, this.availableCategories); - parameter.Add(p => p.ShowCheckBoxes, true); - parameter.Add(p => p.MaxNumberOfChips, 2); - parameter.Add(p => p.Enabled, true); - }); - } + this.context = new TestContext(); + this.context.ConfigureDevExpressBlazor(); - [Test] - public void VerifyComponent() - { - Assert.Multiple(() => - { - Assert.IsTrue(this.component.Instance.Enabled); - Assert.IsNotEmpty(this.component.Instance.Data); - Assert.IsNotEmpty(this.component.Instance.Values); - Assert.IsTrue(this.component.Instance.ShowCheckBoxes); - }); + this.availableCategories = new List + { + new() { Name = "Category" } + }; - this.component.Render(); - - var comboBox = this.component.FindComponent>(); + this.component = this.context.RenderComponent>(parameter => + { + parameter.Add(p => p.Data, this.availableCategories); + parameter.Add(p => p.Values, this.availableCategories); + parameter.Add(p => p.ShowCheckBoxes, true); + parameter.Add(p => p.MaxNumberOfChips, 2); + parameter.Add(p => p.Enabled, true); + }); + } - Assert.Multiple(() => + [Test] + public void VerifyComponent() { - Assert.IsNotNull(comboBox); - }); + Assert.Multiple(() => + { + Assert.IsTrue(this.component.Instance.Enabled); + Assert.IsNotEmpty(this.component.Instance.Data); + Assert.IsNotEmpty(this.component.Instance.Values); + Assert.IsTrue(this.component.Instance.ShowCheckBoxes); + }); + + this.component.Render(); + + var comboBox = this.component.FindComponent>(); + + Assert.Multiple(() => { Assert.IsNotNull(comboBox); }); + } } -} +} \ No newline at end of file From bdb8164d60c5272223b8d42e62fcac399fa131e7 Mon Sep 17 00:00:00 2001 From: Robbware Date: Tue, 3 Oct 2023 17:02:16 +0100 Subject: [PATCH 07/10] Add missing properties from the general MultiComboBox coverage. --- .../Components/MultiComboBoxTestFixture.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs index d0344af6..c0071508 100644 --- a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs +++ b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs @@ -33,8 +33,6 @@ namespace COMET.Web.Common.Tests.Components using DevExpress.Blazor; - using Microsoft.AspNetCore.Authorization.Infrastructure; - using TestContext = Bunit.TestContext; using NUnit.Framework; @@ -64,6 +62,15 @@ public void SetUp() parameter.Add(p => p.ShowCheckBoxes, true); parameter.Add(p => p.MaxNumberOfChips, 2); parameter.Add(p => p.Enabled, true); + + parameter.Add(p => p.EditorTextTemplate, builder => + { + builder.OpenElement(0, "span"); + builder.AddContent(1, ""); + builder.CloseElement(); + }); + + parameter.Add(p => p.RowTemplate, value => value.Name); }); } @@ -76,13 +83,13 @@ public void VerifyComponent() Assert.IsNotEmpty(this.component.Instance.Data); Assert.IsNotEmpty(this.component.Instance.Values); Assert.IsTrue(this.component.Instance.ShowCheckBoxes); + Assert.IsNotNull(this.component.Instance.EditorTextTemplate); }); - + this.component.Render(); var comboBox = this.component.FindComponent>(); - - Assert.Multiple(() => { Assert.IsNotNull(comboBox); }); + Assert.IsNotNull(comboBox); } } } \ No newline at end of file From 3bc2e9b369e800c033b5e3693cdb1c72ddf9a891 Mon Sep 17 00:00:00 2001 From: Robbware Date: Tue, 3 Oct 2023 17:15:02 +0100 Subject: [PATCH 08/10] Add specific assertion of data on the ItemTemplate for the MultiComboBox test. --- .../Components/MultiComboBoxTestFixture.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs index c0071508..3ccdf6ec 100644 --- a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs +++ b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs @@ -90,6 +90,13 @@ public void VerifyComponent() var comboBox = this.component.FindComponent>(); Assert.IsNotNull(comboBox); + + var comboBoxItemTemplate = (MultiComboBox) comboBox.Instance.ItemTemplate.Target; + + Assert.IsTrue(comboBoxItemTemplate.Enabled); + Assert.IsNotEmpty(comboBoxItemTemplate.Data); + Assert.IsNotEmpty(comboBoxItemTemplate.Values); + Assert.IsNotNull(comboBoxItemTemplate.RowTemplate); } } } \ No newline at end of file From ee6e9020041b7542f8807f048c5dc0002c06764b Mon Sep 17 00:00:00 2001 From: Robbware Date: Tue, 3 Oct 2023 18:07:06 +0100 Subject: [PATCH 09/10] Adding more specific checks (WIP). --- .../Components/MultiComboBoxTestFixture.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs index 3ccdf6ec..5a6e7bf4 100644 --- a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs +++ b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs @@ -33,6 +33,8 @@ namespace COMET.Web.Common.Tests.Components using DevExpress.Blazor; + using Microsoft.AspNetCore.Components; + using TestContext = Bunit.TestContext; using NUnit.Framework; @@ -84,19 +86,17 @@ public void VerifyComponent() Assert.IsNotEmpty(this.component.Instance.Values); Assert.IsTrue(this.component.Instance.ShowCheckBoxes); Assert.IsNotNull(this.component.Instance.EditorTextTemplate); + Assert.That(this.component.Instance.MaxNumberOfChips, Is.EqualTo(2)); + Assert.IsNotNull(this.component.Instance.RowTemplate); }); - this.component.Render(); - var comboBox = this.component.FindComponent>(); Assert.IsNotNull(comboBox); - - var comboBoxItemTemplate = (MultiComboBox) comboBox.Instance.ItemTemplate.Target; + Assert.IsNull(comboBox.Instance.Value); - Assert.IsTrue(comboBoxItemTemplate.Enabled); - Assert.IsNotEmpty(comboBoxItemTemplate.Data); - Assert.IsNotEmpty(comboBoxItemTemplate.Values); - Assert.IsNotNull(comboBoxItemTemplate.RowTemplate); + var dropdownItems = this.component.FindAll(".chip"); + Assert.IsNotNull(dropdownItems); + Assert.IsNotEmpty(dropdownItems); } } } \ No newline at end of file From 8d464e28b4b4c23615633b63b89365285244f93f Mon Sep 17 00:00:00 2001 From: Robbware Date: Wed, 4 Oct 2023 09:45:18 +0100 Subject: [PATCH 10/10] Add specific assertions and a behavior to open the dropdown. --- .../Components/MultiComboBoxTestFixture.cs | 13 ++++++++++--- COMET.Web.Common/Components/MultiComboBox.razor | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs index 5a6e7bf4..c998a6b3 100644 --- a/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs +++ b/COMET.Web.Common.Tests/Components/MultiComboBoxTestFixture.cs @@ -54,7 +54,11 @@ public void SetUp() this.availableCategories = new List { - new() { Name = "Category" } + new() { Name = "Category" }, + new() { Name = "Category2" }, + new() { Name = "Category3" }, + new() { Name = "Category4" }, + new() { Name = "Category5" }, }; this.component = this.context.RenderComponent>(parameter => @@ -77,7 +81,7 @@ public void SetUp() } [Test] - public void VerifyComponent() + public async Task VerifyComponent() { Assert.Multiple(() => { @@ -94,9 +98,12 @@ public void VerifyComponent() Assert.IsNotNull(comboBox); Assert.IsNull(comboBox.Instance.Value); - var dropdownItems = this.component.FindAll(".chip"); + await this.component.InvokeAsync(() => comboBox.Instance.ShowDropDown()); + + var dropdownItems = this.component.FindAll(".item-template-checkbox"); Assert.IsNotNull(dropdownItems); Assert.IsNotEmpty(dropdownItems); + Assert.AreEqual(this.availableCategories.Count, dropdownItems.Count); } } } \ No newline at end of file diff --git a/COMET.Web.Common/Components/MultiComboBox.razor b/COMET.Web.Common/Components/MultiComboBox.razor index af62e64f..926ad8e3 100644 --- a/COMET.Web.Common/Components/MultiComboBox.razor +++ b/COMET.Web.Common/Components/MultiComboBox.razor @@ -34,7 +34,7 @@ @if (this.ShowCheckBoxes) { var isSelected = this.Values.Contains(context); - + } @if (this.RowTemplate != null)