Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions DotnetViewComponents/ViewComponents/TaskListViewComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace DotnetViewComponents.ViewComponents
{
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using DotnetViewComponents.ViewModels;

public class TaskListViewComponent : ViewComponent
{
public IViewComponentResult Invoke(
IEnumerable<TaskListItemViewModel> listItems)
{
var model = new TaskListViewModel(listItems);

return View(model);
}
}
}
86 changes: 86 additions & 0 deletions DotnetViewComponents/ViewModels/TaskListItemViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
namespace DotnetViewComponents.ViewModels
{
public class TaskListItemViewModel
{
/// <summary>
/// Creates a new instance of the <see cref="TaskListItemViewModel"/> class.
/// STATE: Choose between "Completed", "Incomplete" and "Cannot start yet".
/// STATESTYLE: The style should match the state. (e.g. "TaskStateStyle.INCOMPLETE" for "Incomplete", "TaskStateStyle.COMPLETED" for "Completed", "TaskStateStyle.UNSTARTED" for "Cannot start yet")
/// </summary>
/// <param name="id">The unique identifier for the task.</param>
/// <param name="name">The name of the task.</param>
/// <param name="aspController">The asp-controller associated with the task's action.</param>
/// <param name="aspAction">The asp-action action associated with the task's action.</param>
/// <param name="aspRouteData">An asp-all-data-route dictionary containing route data for the task's action.</param>
/// <param name="state">Choose between "Completed", "Incomplete" and "Cannot start yet".</param>
/// <param name="stateStyle">The style should match the state. (e.g. "TaskStateStyle.INCOMPLETE" for "Incomplete", "TaskStateStyle.COMPLETED" for "Completed", "TaskStateStyle.UNSTARTED" for "Cannot start yet")</param>
/// <param name="hintText">Additional text providing hints about the task.</param>
public TaskListItemViewModel(
string id,
string name,
string aspController,
string aspAction,
Dictionary<string, string> aspRouteData,
string state,
TaskStateStyle stateStyle,
string hintText)
{
Id = id;
Name = name;
AspController = aspController;
AspAction = aspAction;
AspRouteData = aspRouteData;
State = state;
StateStyle = stateStyle;
HintText = hintText;
}

/// <summary>
/// Gets or sets the unique identifier for the task.
/// </summary>
public string Id { get; set; }

/// <summary>
/// Gets or sets the name of the task.
/// </summary>
public string Name { get; set; }

/// <summary>
/// Gets or sets the asp-controller of the link.
/// </summary>
public string AspController { get; set; }

/// <summary>
/// Gets or sets the as-action of the link.
/// </summary>
public string AspAction { get; set; }

/// <summary>
/// Gets or sets the asp-all-route-data of the link.
/// </summary>
public Dictionary<string, string> AspRouteData { get; set; }

/// <summary>
/// Gets or sets the current state of the task.
/// </summary>
public string State { get; set; }

/// <summary>
/// Gets or sets the style that matches the state of the task.
/// </summary>
public TaskStateStyle StateStyle { get; set; }

/// <summary>
/// Gets or sets additional text providing hints about the task.
/// </summary>
public string HintText { get; set; }
}


public enum TaskStateStyle
{
COMPLETED,
INCOMPLETE,
UNSTARTED
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a cannot start yet state too:
image
This is rendered in a div of class="nhsuk-task-list__status nhsuk-task-list__status--cannot-start-yet"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see in the Razor partial that this is covered by "UNSTARTED". It still might be worth checking with Allan that we don't need more flexibility in terms of the styling of tags inside the div.

}
}
20 changes: 20 additions & 0 deletions DotnetViewComponents/ViewModels/TaskListViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace DotnetViewComponents.ViewModels
{
public class TaskListViewModel
{
/// <summary>
/// Initializes a new instance of the <see cref="TaskListViewModel"/> class.
/// </summary>
/// <param name="listItems">A collection of task list items.</param>
public TaskListViewModel(
IEnumerable<TaskListItemViewModel> listItems)
{
ListItems = listItems;
}

/// <summary>
/// Gets or sets the collection of task list items.
/// </summary>
public IEnumerable<TaskListItemViewModel> ListItems { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@using DotnetViewComponents.ViewModels
@model TaskListViewModel

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest simplifying the Razor markup by setting up some variables:

var hasLink = task.StateStyle != TaskStateStyle.UNSTARTED;
    var statusClass = task.StateStyle switch
    {
        TaskStateStyle.COMPLETED => "nhsuk-task-list__status nhsuk-task-list__status--completed",
        TaskStateStyle.INCOMPLETE => "nhsuk-task-list__status",
        TaskStateStyle.UNSTARTED => "nhsuk-task-list__status nhsuk-task-list__status--cannot-start-yet",
        _ => "nhsuk-task-list__status"
    };

<ul class="nhsuk-task-list">
@foreach (var task in Model.ListItems)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we parameterise some of the properties we can simplify this (to avoid repetition) markup to something like:

@foreach (var task in Model.ListItems)
{   
    <li class="nhsuk-task-list__item @(hasLink ? "nhsuk-task-list__item--with-link" : "")">
        <div class="nhsuk-task-list__name-and-hint">
            @if (hasLink)
            {
                <a class="nhsuk-link nhsuk-task-list__link" href="@task.Url" aria-describedby="@statusId">
                    @task.Name
                </a>
            }
            else
            {
                <div>@task.Name</div>
            }
            @if (!String.IsNullOrWhiteSpace(task.HintText))
            {
                <div id="your-health-3-hint" class="nhsuk-task-list__hint">@task.HintText</div>
            }
        </div>
        <div class="@statusClass" id="@statusId">
            @if (task.StateStyle == TaskStateStyle.INCOMPLETE)
            {
                <strong class="nhsuk-tag nhsuk-tag--blue">@task.State</strong>
            }
            else
            {
                @task.State
            }
        </div>
    </li>
}

{
var hasLink = task.StateStyle != TaskStateStyle.UNSTARTED;
var statusClass = task.StateStyle switch
{
TaskStateStyle.COMPLETED => "nhsuk-task-list__status nhsuk-task-list__status--completed",
TaskStateStyle.INCOMPLETE => "nhsuk-task-list__status",
TaskStateStyle.UNSTARTED => "nhsuk-task-list__status nhsuk-task-list__status--cannot-start-yet",
_ => "nhsuk-task-list__status"
};
var statusId = task.StateStyle == TaskStateStyle.UNSTARTED
? $"task-list-{task.Id}-status"
: task.Id;


<li class="nhsuk-task-list__item @(hasLink ? "nhsuk-task-list__item--with-link" : "")">
<div class="nhsuk-task-list__name-and-hint">
@if (hasLink)
{
<a class="nhsuk-link nhsuk-task-list__link" asp-controller="@task.AspController" asp-action="@task.AspAction" asp-all-route-data="@task.AspRouteData" aria-describedby="@statusId">
@task.Name
</a>
}
else
{
<div>@task.Name</div>
}
@if (!String.IsNullOrWhiteSpace(task.HintText))
{
<div id="@statusId" class="nhsuk-task-list__hint">@task.HintText</div>
}
</div>
<div class="@statusClass" id="@statusId">
@if (task.StateStyle == TaskStateStyle.INCOMPLETE)
{
<strong class="nhsuk-tag nhsuk-tag--blue">@task.State</strong>
}
else
{
@task.State
}
</div>
</li>
}
</ul>