|
@@ -1,16 +1,20 @@
|
|
|
@using RackPeek.Domain.UseCases.Tags
|
|
@using RackPeek.Domain.UseCases.Tags
|
|
|
@typeparam TResource where TResource : RackPeek.Domain.Resources.Resource
|
|
@typeparam TResource where TResource : RackPeek.Domain.Resources.Resource
|
|
|
@inject NavigationManager Nav
|
|
@inject NavigationManager Nav
|
|
|
-
|
|
|
|
|
@inject IAddTagUseCase<TResource> AddTagUseCase
|
|
@inject IAddTagUseCase<TResource> AddTagUseCase
|
|
|
@inject IRemoveTagUseCase<TResource> RemoveTagUseCase
|
|
@inject IRemoveTagUseCase<TResource> RemoveTagUseCase
|
|
|
|
|
|
|
|
-<div class="md:col-span-2">
|
|
|
|
|
- <div class="flex items-center justify-between mb-1 group">
|
|
|
|
|
|
|
+<div class="md:col-span-2"
|
|
|
|
|
+ data-testid="@BaseTestId">
|
|
|
|
|
+
|
|
|
|
|
+ <div class="flex items-center justify-between mb-1 group"
|
|
|
|
|
+ data-testid="@($"{BaseTestId}-header")">
|
|
|
|
|
+
|
|
|
<div class="text-zinc-400">
|
|
<div class="text-zinc-400">
|
|
|
Tags
|
|
Tags
|
|
|
<button class="hover:text-emerald-400 ml-1"
|
|
<button class="hover:text-emerald-400 ml-1"
|
|
|
title="Add Tag"
|
|
title="Add Tag"
|
|
|
|
|
+ data-testid="@($"{BaseTestId}-add")"
|
|
|
@onclick="OpenAddTag">
|
|
@onclick="OpenAddTag">
|
|
|
+
|
|
+
|
|
|
</button>
|
|
</button>
|
|
@@ -19,23 +23,26 @@
|
|
|
|
|
|
|
|
@if (Resource.Tags.Any())
|
|
@if (Resource.Tags.Any())
|
|
|
{
|
|
{
|
|
|
- <div class="flex flex-wrap gap-2">
|
|
|
|
|
|
|
+ <div class="flex flex-wrap gap-2"
|
|
|
|
|
+ data-testid="@($"{BaseTestId}-list")">
|
|
|
|
|
+
|
|
|
@foreach (var tag in Resource.Tags.OrderBy(t => t))
|
|
@foreach (var tag in Resource.Tags.OrderBy(t => t))
|
|
|
{
|
|
{
|
|
|
- <div class="flex text-xs rounded overflow-hidden border border-zinc-700">
|
|
|
|
|
|
|
+ <div class="flex text-xs rounded overflow-hidden border border-zinc-700"
|
|
|
|
|
+ data-testid="@($"{BaseTestId}-tag-{tag}")">
|
|
|
|
|
|
|
|
- <!-- LEFT SIDE (Navigate) -->
|
|
|
|
|
<button type="button"
|
|
<button type="button"
|
|
|
class="px-2 py-0.5 bg-zinc-800 text-zinc-300 hover:bg-emerald-800 hover:text-emerald-200 transition"
|
|
class="px-2 py-0.5 bg-zinc-800 text-zinc-300 hover:bg-emerald-800 hover:text-emerald-200 transition"
|
|
|
title="View tag"
|
|
title="View tag"
|
|
|
|
|
+ data-testid="@($"{BaseTestId}-tag-{tag}-view")"
|
|
|
@onclick="() => NavigateToTag(tag)">
|
|
@onclick="() => NavigateToTag(tag)">
|
|
|
@tag
|
|
@tag
|
|
|
</button>
|
|
</button>
|
|
|
|
|
|
|
|
- <!-- RIGHT SIDE (Delete) -->
|
|
|
|
|
<button type="button"
|
|
<button type="button"
|
|
|
class="px-1 py-0.5 bg-zinc-800 text-zinc-400 hover:bg-red-800 hover:text-red-200 transition border-l border-zinc-700"
|
|
class="px-1 py-0.5 bg-zinc-800 text-zinc-400 hover:bg-red-800 hover:text-red-200 transition border-l border-zinc-700"
|
|
|
title="Remove tag"
|
|
title="Remove tag"
|
|
|
|
|
+ data-testid="@($"{BaseTestId}-tag-{tag}-remove")"
|
|
|
@onclick="() => RemoveTag(tag)">
|
|
@onclick="() => RemoveTag(tag)">
|
|
|
✕
|
|
✕
|
|
|
</button>
|
|
</button>
|
|
@@ -47,37 +54,40 @@
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
-<StringValueModal
|
|
|
|
|
|
|
+<CommaSeparatedStringModal
|
|
|
IsOpen="_tagModalOpen"
|
|
IsOpen="_tagModalOpen"
|
|
|
IsOpenChanged="v => _tagModalOpen = v"
|
|
IsOpenChanged="v => _tagModalOpen = v"
|
|
|
- Title="Add Tag"
|
|
|
|
|
- Description="Enter tag value"
|
|
|
|
|
- Label="Tag"
|
|
|
|
|
- Value=""
|
|
|
|
|
- OnSubmit="HandleTagSubmit"/>
|
|
|
|
|
|
|
+ Title="Add Tags"
|
|
|
|
|
+ Description="Enter one or more tags separated by commas"
|
|
|
|
|
+ Label="Tags"
|
|
|
|
|
+ TestIdPrefix="@BaseTestId"
|
|
|
|
|
+ OnSubmit="HandleTagsSubmit" />
|
|
|
|
|
|
|
|
@code {
|
|
@code {
|
|
|
[Parameter][EditorRequired] public TResource Resource { get; set; } = default!;
|
|
[Parameter][EditorRequired] public TResource Resource { get; set; } = default!;
|
|
|
-
|
|
|
|
|
[Parameter] public EventCallback OnTagsChanged { get; set; }
|
|
[Parameter] public EventCallback OnTagsChanged { get; set; }
|
|
|
|
|
+ [Parameter] public string? TestIdPrefix { get; set; }
|
|
|
|
|
|
|
|
- bool _tagModalOpen;
|
|
|
|
|
|
|
+ private bool _tagModalOpen;
|
|
|
|
|
+
|
|
|
|
|
+ private string BaseTestId =>
|
|
|
|
|
+ string.IsNullOrWhiteSpace(TestIdPrefix)
|
|
|
|
|
+ ? "resource-tag-editor"
|
|
|
|
|
+ : $"{TestIdPrefix}-resource-tag-editor";
|
|
|
|
|
|
|
|
void OpenAddTag()
|
|
void OpenAddTag()
|
|
|
{
|
|
{
|
|
|
_tagModalOpen = true;
|
|
_tagModalOpen = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async Task HandleTagSubmit(string value)
|
|
|
|
|
|
|
+ public async Task HandleTagsSubmit(IReadOnlyList<string> values)
|
|
|
{
|
|
{
|
|
|
- if (string.IsNullOrWhiteSpace(value))
|
|
|
|
|
- return;
|
|
|
|
|
-
|
|
|
|
|
- await AddTagUseCase.ExecuteAsync(
|
|
|
|
|
- Resource.Name,
|
|
|
|
|
- value);
|
|
|
|
|
-
|
|
|
|
|
- _tagModalOpen = false;
|
|
|
|
|
|
|
+ foreach (var value in values)
|
|
|
|
|
+ {
|
|
|
|
|
+ await AddTagUseCase.ExecuteAsync(
|
|
|
|
|
+ Resource.Name,
|
|
|
|
|
+ value);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (OnTagsChanged.HasDelegate)
|
|
if (OnTagsChanged.HasDelegate)
|
|
|
await OnTagsChanged.InvokeAsync();
|
|
await OnTagsChanged.InvokeAsync();
|
|
@@ -97,5 +107,4 @@
|
|
|
{
|
|
{
|
|
|
Nav.NavigateTo($"tags/{Uri.EscapeDataString(tag)}");
|
|
Nav.NavigateTo($"tags/{Uri.EscapeDataString(tag)}");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|