Skip to content

Commit

Permalink
Fixed #508; Fixed #528; Fixed #532 (#573)
Browse files Browse the repository at this point in the history
* Introduced new dialog parameter that made it possible to delete task directly in dialog

* Edited UnsavedChanges in LearningSpaceViewModel.cs to consider changes of StoryElements

* Fixed RemoveTask in AdaptivityContentDialog.razor

* Adjusted tests in AdaptivityContentDialogIt.cs
  • Loading branch information
MarvinHo64 authored Jul 26, 2024
1 parent 4feb9aa commit c919560
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public void Execute()
"Deleted AdaptivityTask {AdaptivityTaskName} ({AdaptivityTaskId}) in AdaptivityContent {AdaptivityContentName}",
AdaptivityTask.Name, AdaptivityTask.Id, AdaptivityContent.Name);
MappingAction.Invoke(AdaptivityContent);
AdaptivityContent.UnsavedChanges = true;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
using System.Threading.Tasks;
using AutoMapper;
using Bunit;
using BusinessLogic.Entities.LearningContent.Adaptivity;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.DependencyInjection;
using MudBlazor;
using NSubstitute;
using NUnit.Framework;
using Presentation.Components.Adaptivity.Dialogues;
using Presentation.Components.Forms;
using Presentation.PresentationLogic.API;
using Presentation.PresentationLogic.LearningContent;
using Presentation.PresentationLogic.LearningContent.AdaptivityContent;
using Presentation.PresentationLogic.LearningContent.AdaptivityContent.Question;
using Shared.Adaptivity;
Expand All @@ -28,25 +27,26 @@ public void Setup()
{
PresentationLogic = Substitute.For<IPresentationLogic>();
Context.Services.AddSingleton(PresentationLogic);
FormDataContainer = Substitute.For<IFormDataContainer<AdaptivityContentFormModel, AdaptivityContent>>();
FormDataContainer.FormModel.Returns(new AdaptivityContentFormModel());
Context.Services.AddSingleton(FormDataContainer);
AdaptivityContent = FormModelProvider.GetAdaptivityContent();
AdaptivityContentFormModel = FormModelProvider.GetAdaptivityContent();
Tasks = new List<IAdaptivityTaskViewModel>();
AdaptivityContent.Tasks = Tasks;
AdaptivityContentFormModel.Tasks = Tasks;
Mapper = Substitute.For<IMapper>();
Mapper.When(x => x.Map(Arg.Any<IAdaptivityContentViewModel>(), Arg.Any<AdaptivityContentFormModel>())).Do(y =>
Mapper.When(x => x.Map(Arg.Any<AdaptivityContentFormModel>(), Arg.Any<ILearningContentViewModel>())).Do(y =>
{
y.Arg<AdaptivityContentFormModel>().Tasks = Tasks;
});
Context.Services.AddSingleton(Mapper);
PresentationLogic.When(x => x.CreateAdaptivityTask(AdaptivityContent, Arg.Any<string>())).Do(y =>
PresentationLogic.When(x => x.CreateAdaptivityTask(AdaptivityContentFormModel, Arg.Any<string>())).Do(y =>
{
var task = Substitute.For<IAdaptivityTaskViewModel>();
task.Name.Returns(y.ArgAt<string>(1));
Tasks.Add(task);
});
PresentationLogic.When(x => x.DeleteAdaptivityTask(AdaptivityContent, Arg.Any<IAdaptivityTaskViewModel>())).Do(
PresentationLogic.When(x =>
x.DeleteAdaptivityTask(AdaptivityContentFormModel, Arg.Any<IAdaptivityTaskViewModel>())).Do(
y => { Tasks.Remove(y.ArgAt<IAdaptivityTaskViewModel>(1)); });
PresentationLogic.When(x =>
x.DeleteAdaptivityTask(Arg.Any<AdaptivityContentViewModel>(), Arg.Any<IAdaptivityTaskViewModel>())).Do(
y => { Tasks.Remove(y.ArgAt<IAdaptivityTaskViewModel>(1)); });
Context.ComponentFactories.AddStub<AdaptivityContentDialogRuleControl>();
}
Expand All @@ -55,25 +55,35 @@ public void Setup()
public void Teardown()
{
DialogProvider.Dispose();
ContentToEdit = null!;
}

private void SetupWithContentToEdit()
{
Setup();
ContentToEdit = (AdaptivityContentViewModel)ViewModelProvider.GetAdaptivityContent();
((AdaptivityContentViewModel)ContentToEdit).Tasks = Tasks;
}

private IDialogReference Dialog { get; set; } = null!;
private AdaptivityContentFormModel AdaptivityContent { get; set; } = null!;
private AdaptivityContentFormModel AdaptivityContentFormModel { get; set; } = null!;
private List<IAdaptivityTaskViewModel> Tasks { get; set; } = null!;
private IPresentationLogic PresentationLogic { get; set; } = null!;
private IMapper Mapper { get; set; } = null!;
private IFormDataContainer<AdaptivityContentFormModel, AdaptivityContent> FormDataContainer { get; set; } = null!;

private ILearningContentViewModel ContentToEdit { get; set; } = null!;

private async Task GetDialogAsync()
{
var dialogParameters = new DialogParameters
{
{ nameof(AdaptivityContentDialog.MyContent), AdaptivityContent },
{ nameof(AdaptivityContentDialog.MyContent), AdaptivityContentFormModel },
{ nameof(AdaptivityContentDialog.ContentToEdit), ContentToEdit },
{ nameof(AdaptivityContentDialog.DebounceInterval), 10 }
};
Dialog = await OpenDialogAndGetDialogReferenceAsync(options: new DialogOptions(),
parameters: dialogParameters);
Mapper.Received(1).Map(AdaptivityContent, FormDataContainer.FormModel);
Mapper.Received(1).Map(AdaptivityContentFormModel, ContentToEdit);
Mapper.ClearReceivedCalls();
}

Expand All @@ -84,22 +94,38 @@ public async Task AddTaskButtonClicked_CallsPresentationLogicAndMapper()
await GetDialogAsync();
var button = DialogProvider.FindComponent<MudIconButton>().Find("button");
await button.ClickAsync(new MouseEventArgs());
PresentationLogic.Received(1).CreateAdaptivityTask(AdaptivityContent, "AdaptivityContentDialog.NewTask.Name1");
Mapper.Received(1).Map(AdaptivityContent, FormDataContainer.FormModel);
PresentationLogic.Received(1)
.CreateAdaptivityTask(AdaptivityContentFormModel, "AdaptivityContentDialog.NewTask.Name1");
Mapper.Received(1).Map(AdaptivityContentFormModel, ContentToEdit);
}

[Test]
// ANF-ID: [AWA0007]
public async Task DeleteTaskButtonClicked_ContentToEditNull_CallsPresentationLogic()
{
await GetDialogAsync();
var button = DialogProvider.FindComponent<MudIconButton>().Find("button");
await button.ClickAsync(new MouseEventArgs());
var buttons = DialogProvider.FindComponents<MudIconButton>();
var deleteButton = buttons[0].Find("button");
var task = AdaptivityContentFormModel.Tasks.Last();
await deleteButton.ClickAsync(new MouseEventArgs());
PresentationLogic.Received(1).DeleteAdaptivityTask(AdaptivityContentFormModel, task);
}

[Test]
// ANF-ID: [AWA0007]
public async Task DeleteTaskButtonClicked_CallsPresentationLogic()
public async Task DeleteTaskButtonClicked_ContentToEditNotNull_CallsPresentationLogic()
{
SetupWithContentToEdit();
await GetDialogAsync();
var button = DialogProvider.FindComponent<MudIconButton>().Find("button");
await button.ClickAsync(new MouseEventArgs());
var buttons = DialogProvider.FindComponents<MudIconButton>();
var deleteButton = buttons[0].Find("button");
var task = AdaptivityContent.Tasks.Last();
var task = ((AdaptivityContentViewModel)ContentToEdit).Tasks.Last();
await deleteButton.ClickAsync(new MouseEventArgs());
PresentationLogic.Received(1).DeleteAdaptivityTask(AdaptivityContent, task);
PresentationLogic.Received(1).DeleteAdaptivityTask((AdaptivityContentViewModel)ContentToEdit, task);
}

[Test]
Expand Down Expand Up @@ -138,7 +164,7 @@ public async Task ChangeRequiredDifficulty_CallsPresentationLogic([Values] bool
task.MinimumRequiredDifficulty.Returns(wasSelectedAsRequired
? QuestionDifficulty.Medium
: null);
AdaptivityContent.Tasks.Add(task);
AdaptivityContentFormModel.Tasks.Add(task);
await GetDialogAsync();
var iconButtons = DialogProvider.FindComponents<MudIconButton>();
var keyButton = iconButtons[3].Find("button");
Expand All @@ -155,7 +181,7 @@ public async Task DeleteQuestionButtonClicked_CallsPresentationLogic()
var question = Substitute.For<IAdaptivityQuestionViewModel>();
question.Difficulty.Returns(QuestionDifficulty.Medium);
task.Questions.Returns(new List<IAdaptivityQuestionViewModel> { question });
AdaptivityContent.Tasks.Add(task);
AdaptivityContentFormModel.Tasks.Add(task);
await GetDialogAsync();
var iconButtons = DialogProvider.FindComponents<MudIconButton>();
var deleteButton = iconButtons[2].Find("button");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
@using System.Diagnostics.CodeAnalysis
@using AutoMapper
@using Microsoft.Extensions.Localization
@using Presentation.PresentationLogic.API
@using Presentation.PresentationLogic.LearningContent
@using Presentation.PresentationLogic.LearningContent.AdaptivityContent
@using Presentation.PresentationLogic.LearningContent.AdaptivityContent.Question
@using Shared.Adaptivity
@using Microsoft.Extensions.Localization
@using AutoMapper
@using BusinessLogic.Entities.LearningContent.Adaptivity
@using Presentation.Components.Forms
@using System.Diagnostics.CodeAnalysis
<p class="text-sm 2xl:text-base mx-6">@Localizer["AdaptivityContentDialog.Subtitle"]</p>
<MudTable @ref="@_table" Items="@MyContent.Tasks" Height="500px" Outlined="true" Class="rounded-lg border-4 border-adlerdarkblue-100 m-4"
FixedHeader="true" r="false" CustomFooter="true" Bordered="true">
Expand Down Expand Up @@ -43,13 +42,13 @@
<MudTd DataLabel="@Localizer["AdaptivityContentDialog.Table.Header.Tasks"]">
<MudForm Model="context" @ref="_nameMudFormRefs[context]">
<span class="inline-flex min-w-full">
<MudTextField Class="break-words"
T="string"
<MudTextField Class="break-words"
T="string"
@bind-Value="@_nameValues[context]"
Lines="2"
Required="true"
RequiredError="@Localizer["AdaptivityContentDialog.Table.Field.Name.RequiredError.Text"]"
DebounceInterval="DebounceInterval"
DebounceInterval="DebounceInterval"
OnDebounceIntervalElapsed="s => RenameTask(context, s)"/>
<MudIconButton Title="@Localizer["AdaptivityContent.Dialog.Table.Header.DeleteTask"]" Icon="@Icons.Material.Filled.Remove" OnClick="() => RemoveTask(context)"/>
</span>
Expand All @@ -63,10 +62,10 @@
@foreach (var difficulty in Enum.GetValues(typeof(QuestionDifficulty)).Cast<QuestionDifficulty>())
{
<MudTd Class="p-1" DataLabel="@Localizer["AdaptivityContentDialog.Table.Header." + difficulty]">
@if (GetTaskQuestionWithQuestionDifficulty(context, difficulty) is {} question)
@if (GetTaskQuestionWithQuestionDifficulty(context, difficulty) is { } question)
{
<div class="flex flex-row justify-between">

<div class="flex items-center justify-center">
<MudTooltip Placement="Placement.Top" Duration="double.Epsilon" Style="background: transparent;">
<ChildContent>
Expand All @@ -79,25 +78,25 @@
</TooltipContent>
</MudTooltip>
</div>

<div style="vertical-align: top; align-items: center;" class="flex flex-col justify-center items-center">
<MudButton Class="shadow btn-standard px-1 w-36 2xl:w-40 mt-2 relative" OnClick="() => AddOrEditQuestion(context, difficulty)">
<MudText Class="flex justify-start items-center uppercase font-bold text-xs 2xl:text-sm">@Localizer["AdaptivityContentDialog.Table.Button.Question." + difficulty]</MudText>
<div class="flex justify-end absolute w-2.5 h-1.5 top-0.5 -right-0.5 m-0.5">
<MudIconButton Title="@Localizer["AdaptivityContentDialog.Table.Button.Question.Delete"]" Class="w-2 h-2 close-button rounded-full text-adlerdarkblue bg-adlerblue-200 p-3 transform ease-in-out duration-75 active:drop-shadow-none hover:bg-adlerblue-200 hover:text-adlerdarkblue" Size="Size.Small" Icon="@Icons.Material.Filled.Delete" OnClick="() => RemoveQuestion(context, difficulty)"></MudIconButton>
</div>
</MudButton>

<MudButton Class="shadow btn-standard px-1 w-36 2xl:w-40 mt-2 relative" OnClick="() => AddOrEditQuestion(context, difficulty)">
<MudText Class="flex justify-start items-center uppercase font-bold text-xs 2xl:text-sm">@Localizer["AdaptivityContentDialog.Table.Button.Question." + difficulty]</MudText>
<div class="flex justify-end absolute w-2.5 h-1.5 top-0.5 -right-0.5 m-0.5">
<MudIconButton Title="@Localizer["AdaptivityContentDialog.Table.Button.Question.Delete"]" Class="w-2 h-2 close-button rounded-full text-adlerdarkblue bg-adlerblue-200 p-3 transform ease-in-out duration-75 active:drop-shadow-none hover:bg-adlerblue-200 hover:text-adlerdarkblue" Size="Size.Small" Icon="@Icons.Material.Filled.Delete" OnClick="() => RemoveQuestion(context, difficulty)"></MudIconButton>
</div>
</MudButton>

<MudDivider></MudDivider>
<AdaptivityContentDialogRuleControl Question="question"/>

<AdaptivityContentDialogRuleControl Question="question"/>
</div>

<div class="flex justify-center items-start">
<MudToggleIconButton Toggled="@context.MinimumRequiredDifficulty.Equals(difficulty)"
ToggledChanged="b => ChangeRequiredDifficulty(context, difficulty, b)"
ToggledIcon="@Icons.Material.Filled.Key"
ToggledIcon="@Icons.Material.Filled.Key"
Icon="@Icons.Material.Outlined.KeyOff"
Title="@Localizer["AdaptivityContentDialog.Table.Button.RequiredQuestion"]"/>
</div>
Expand Down Expand Up @@ -144,18 +143,16 @@
[Inject, AllowNull] //can never be null, DI will throw exception on unresolved types
internal IMapper Mapper { get; set; }

[Inject, AllowNull] //can never be null, DI will throw exception on unresolved types
internal IFormDataContainer<AdaptivityContentFormModel, AdaptivityContent> FormDataContainer { get; set; }
[Parameter, EditorRequired] public AdaptivityContentFormModel MyContent { get; set; } = null!;

[Parameter, EditorRequired]
public AdaptivityContentFormModel MyContent { get; set; } = null!;
[Parameter] //can never be null, DI will throw exception on unresolved types - n.stich
public ILearningContentViewModel? ContentToEdit { get; set; } = null;

[Parameter]
public int DebounceInterval { get; set; } = 300;
[Parameter] public int DebounceInterval { get; set; } = 300;


private MudTable<IAdaptivityTaskViewModel>? _table;

private readonly Dictionary<IAdaptivityTaskViewModel, MudForm> _nameMudFormRefs = new();

private readonly Dictionary<IAdaptivityTaskViewModel, string> _nameValues = new();
Expand All @@ -173,7 +170,7 @@

private async Task MapIntoContainer()
{
Mapper.Map(MyContent, FormDataContainer.FormModel);
Mapper.Map(MyContent, ContentToEdit);
await InvokeAsync(StateHasChanged);
}

Expand All @@ -198,11 +195,21 @@
PresentationLogic.EditAdaptivityTask(task, task.Name, setChecked ? difficulty : null);
}

private void RemoveTask(IAdaptivityTaskViewModel task)
private async Task RemoveTask(IAdaptivityTaskViewModel task)
{
PresentationLogic.DeleteAdaptivityTask(MyContent, task);
if (ContentToEdit != null && ContentToEdit is AdaptivityContentViewModel adaptivityContentViewModel)
{
PresentationLogic.DeleteAdaptivityTask(adaptivityContentViewModel, task);
Mapper.Map(ContentToEdit, MyContent);
}
else
{
PresentationLogic.DeleteAdaptivityTask(MyContent, task);
}

_nameValues.Remove(task);
_nameMudFormRefs.Remove(task);
await MapIntoContainer();
}

private async Task AddOrEditQuestion(IAdaptivityTaskViewModel task, QuestionDifficulty difficulty)
Expand All @@ -217,16 +224,16 @@
};
var dialog = await DialogService.ShowAsync<AdaptivityQuestionDialog>("", new DialogParameters()
{
{nameof(AdaptivityQuestionDialog.Task), task},
{nameof(AdaptivityQuestionDialog.Difficulty), difficulty}
{ nameof(AdaptivityQuestionDialog.Task), task },
{ nameof(AdaptivityQuestionDialog.Difficulty), difficulty }
}, options);
var result = await dialog.Result;
await InvokeAsync(StateHasChanged);
}

private void RemoveQuestion(IAdaptivityTaskViewModel task, QuestionDifficulty difficulty)
{
if (task.Questions.FirstOrDefault(x => x.Difficulty == difficulty) is {} question)
if (task.Questions.FirstOrDefault(x => x.Difficulty == difficulty) is { } question)
{
PresentationLogic.DeleteAdaptivityQuestion(task, question);
}
Expand Down
5 changes: 3 additions & 2 deletions Presentation/Components/Forms/Element/EditElementForm.razor
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@
{
<div class="px-4">
<Collapsable
Title=@Localizer["EditElementForm.Fields.Collapsable.Goals.Title"] InitiallyCollapsed="true">
Title=@Localizer["EditElementForm.Fields.Collapsable.Goals.Title"] InitiallyCollapsed="true">
<div class="rounded-lg w-4/4 px-2 2xl:px-4 pb-2">
<MudText
Typo="Typo.caption">
Expand Down Expand Up @@ -446,7 +446,8 @@
};
var parameters = new DialogParameters
{
{ nameof(AdaptivityContentDialog.MyContent), FormDataContainer.FormModel.LearningContent }
{ nameof(AdaptivityContentDialog.MyContent), FormDataContainer.FormModel.LearningContent },
{ nameof(AdaptivityContentDialog.ContentToEdit), ElementToEdit.LearningContent }
};
var dialog = await DialogService.ShowAsync<AdaptivityContentDialog>(Localizer["EditElementForm.AdaptivityContent.Dialog.Title"], parameters, options);
_ = await dialog.Result;
Expand Down
Loading

0 comments on commit c919560

Please sign in to comment.