Merge branch 'UI-cleanup' into 'master'
added dynamic components, cleaned up UI See merge request murphg62/2023-ca400-murphg62-byrnm257!15
This commit is contained in:
commit
5308961541
@ -1,13 +1,25 @@
|
||||
public class Component {
|
||||
public int Type {get; set;}
|
||||
public string Text {get; set;}
|
||||
public Stats[] Stats {get; set;}
|
||||
using System.Collections.Generic;
|
||||
public class TableComponent {
|
||||
public int Id {get; set;}
|
||||
public List<Dictionary<string, object>> Data { get; set; }
|
||||
|
||||
|
||||
public TableComponent(int id, List<Dictionary<string, object>> data){
|
||||
Id = id;
|
||||
Data = data;
|
||||
}
|
||||
}
|
||||
|
||||
public Component(int type, string text, Stats[] stats){
|
||||
Type = type;
|
||||
Text = text;
|
||||
Stats = stats;
|
||||
public class GraphComponent {
|
||||
public int Id {get; set;}
|
||||
public string Graph {get; set;}
|
||||
public string Title {get; set;}
|
||||
public List<Stats> Stats { get; set; }
|
||||
|
||||
public GraphComponent(int id, string graph, string title, List<Stats> stats){
|
||||
Id = id;
|
||||
Graph = graph;
|
||||
Title = title;
|
||||
Stats = stats;
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
public class Schema {
|
||||
public Component[] components {get; set;}
|
||||
public TableComponent[] tables {get; set;}
|
||||
public GraphComponent[] graphs {get; set;}
|
||||
}
|
||||
68
src/PanoptesFrontend/Pages/DynamicChart.razor
Normal file
68
src/PanoptesFrontend/Pages/DynamicChart.razor
Normal file
@ -0,0 +1,68 @@
|
||||
@using ApexCharts
|
||||
@using System.Collections.Generic
|
||||
@using PanoptesFrontend.Data;
|
||||
@using System.Timers
|
||||
@implements IDisposable
|
||||
@inject IHttpService httpService
|
||||
@using PanoptesFrontend.Services
|
||||
|
||||
|
||||
<ApexChart @ref=chart TItem="Stats" Title="@ChartTitle">
|
||||
|
||||
<ApexPointSeries TItem="Stats"
|
||||
Items="@StatsList"
|
||||
SeriesType="@SeriesTypes[@SelectedChartType]"
|
||||
Name="Gross Value"
|
||||
XValue="@(e => e.Label)"
|
||||
YValue="@(e => e.Value)"
|
||||
OrderByDescending="e=>e.Y" />
|
||||
</ApexChart>
|
||||
|
||||
@code {
|
||||
private ApexChart<Stats> chart;
|
||||
private bool timerInitialized;
|
||||
private Timer timer;
|
||||
private Dictionary<string, SeriesType> SeriesTypes = new Dictionary<string, SeriesType>
|
||||
{
|
||||
{"line", SeriesType.Line},
|
||||
{"area", SeriesType.Area},
|
||||
{"bar", SeriesType.Bar},
|
||||
{"pie", SeriesType.Pie},
|
||||
{"donut", SeriesType.Donut},
|
||||
{"radial-bar", SeriesType.RadialBar},
|
||||
};
|
||||
[Parameter]
|
||||
public string ChartTitle {get; set;}
|
||||
[Parameter]
|
||||
public List<Stats> StatsList { get; set; }
|
||||
[Parameter]
|
||||
public string SelectedChartType {get; set;}
|
||||
[Parameter]
|
||||
public int ChartId {get; set;}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender && !timerInitialized)
|
||||
{
|
||||
timerInitialized = true;
|
||||
timer = new Timer(5000);
|
||||
timer.Elapsed += async delegate { await UpdateChartSeries(); };
|
||||
timer.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateChartSeries()
|
||||
{
|
||||
string module = "localhost:8080";
|
||||
var response = await httpService.GetAsync<GraphComponent>($"http://localhost:10000/{module}/{ChartId}");
|
||||
StatsList = response.Stats;
|
||||
await chart.UpdateSeriesAsync(true);
|
||||
await InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timer?.Stop();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
77
src/PanoptesFrontend/Pages/DynamicTable.razor
Normal file
77
src/PanoptesFrontend/Pages/DynamicTable.razor
Normal file
@ -0,0 +1,77 @@
|
||||
@using System.Linq.Dynamic.Core
|
||||
@inject IHttpService httpService
|
||||
@using PanoptesFrontend.Services
|
||||
|
||||
<RadzenDataGrid @bind-Value=@selectedItems Data="@data" TItem="IDictionary<string, object>" ColumnWidth="200px"
|
||||
AllowFiltering="true" FilterPopupRenderMode="PopupRenderMode.OnDemand" FilterMode="FilterMode.Advanced" AllowPaging="true" AllowSorting="true">
|
||||
<Columns>
|
||||
@foreach(var column in columns)
|
||||
{
|
||||
<RadzenDataGridColumn TItem="IDictionary<string, object>" Title="@column.Key" Type="column.Value"
|
||||
Property="@GetColumnPropertyExpression(column.Key, column.Value)" >
|
||||
<Template>
|
||||
@context[@column.Key]
|
||||
</Template>
|
||||
</RadzenDataGridColumn>
|
||||
}
|
||||
</Columns>
|
||||
</RadzenDataGrid>
|
||||
<button @onclick="Update">Update Table</button>
|
||||
|
||||
@code {
|
||||
IList<IDictionary<string, object>> selectedItems;
|
||||
|
||||
[Parameter]
|
||||
public List<Dictionary<string, object>> data { get; set; }
|
||||
[Parameter]
|
||||
public int TableId {get; set;}
|
||||
|
||||
public IDictionary<string, Type> columns { get; set; }
|
||||
public string GetColumnPropertyExpression(string name, Type type)
|
||||
{
|
||||
var expression = $@"it[""{name}""].ToString()";
|
||||
|
||||
if (type == typeof(int))
|
||||
{
|
||||
return $"int.Parse({expression})";
|
||||
}
|
||||
else if (type == typeof(DateTime))
|
||||
{
|
||||
return $"DateTime.Parse({expression})";
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Generate the columns dynamically based on the properties of the first object in the list
|
||||
var firstItem = data.FirstOrDefault();
|
||||
if (firstItem != null)
|
||||
{
|
||||
columns = firstItem.ToDictionary(p => p.Key, p => p.Value.GetType());
|
||||
|
||||
foreach (var column in columns.ToList())
|
||||
{
|
||||
if (column.Value != typeof(int) && column.Value != typeof(DateTime))
|
||||
{
|
||||
columns[column.Key] = typeof(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Update()
|
||||
{
|
||||
string module = "localhost:8080";
|
||||
|
||||
var response = await httpService.GetAsync<TableComponent>($"http://localhost:10000/{module}/{TableId}");
|
||||
|
||||
if (response.Data != null)
|
||||
{
|
||||
data.Clear();
|
||||
data.AddRange(response.Data);
|
||||
}
|
||||
await InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
}
|
||||
16
src/PanoptesFrontend/Pages/Logout.razor
Normal file
16
src/PanoptesFrontend/Pages/Logout.razor
Normal file
@ -0,0 +1,16 @@
|
||||
@using PanoptesFrontend.Data.Account;
|
||||
@using PanoptesFrontend.Services;
|
||||
@inject IAccountService AccountService
|
||||
|
||||
|
||||
<button @onclick="LogoutButtonClick">Logout</button>
|
||||
|
||||
|
||||
@code {
|
||||
private string token;
|
||||
private async Task LogoutButtonClick()
|
||||
{
|
||||
token = await AccountService.GetTokenFromLocalStorage();
|
||||
await AccountService.Logout(token);
|
||||
}
|
||||
}
|
||||
58
src/PanoptesFrontend/Pages/ModDisplay.razor
Normal file
58
src/PanoptesFrontend/Pages/ModDisplay.razor
Normal file
@ -0,0 +1,58 @@
|
||||
@page "/test"
|
||||
|
||||
@using System.Net;
|
||||
@using PanoptesFrontend.Data
|
||||
@using PanoptesFrontend.Services
|
||||
@using System.Text.Json;
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@inject IHttpService httpService
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
@if (tables != null | graphs != null)
|
||||
{
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@foreach (var component in tables)
|
||||
{
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<DynamicTable data="@component.Data" TableId="@component.Id"/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@foreach (var component in graphs)
|
||||
{
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<DynamicChart StatsList="@component.Stats"
|
||||
SelectedChartType="@component.Graph"
|
||||
ChartTitle="@component.Title"
|
||||
ChartId="@component.Id"/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
private TableComponent[] tables;
|
||||
private GraphComponent[] graphs;
|
||||
|
||||
private string module = "localhost:8080";
|
||||
|
||||
protected async override Task OnInitializedAsync()
|
||||
{
|
||||
var schema = await httpService.GetAsync<Schema>($"http://localhost:10000/{module}/schema");
|
||||
tables = schema.tables;
|
||||
graphs = schema.graphs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
16
src/PanoptesFrontend/Pages/QueryInput.razor
Normal file
16
src/PanoptesFrontend/Pages/QueryInput.razor
Normal file
@ -0,0 +1,16 @@
|
||||
@inject IHttpService httpService
|
||||
@using PanoptesFrontend.Services
|
||||
|
||||
<RadzenTextArea @bind-Value="sqlQuery" Style="width: 100%; height: 300px" />
|
||||
<button class="btn btn-primary" @onclick="ExecuteSqlQuery">Execute Query</button>
|
||||
|
||||
@code {
|
||||
string sqlQuery;
|
||||
|
||||
private async Task ExecuteSqlQuery()
|
||||
{
|
||||
// Send the SQL query to your backend and handle the response
|
||||
var response = await httpService.PostAsync($"http://localhost:10000/sqlquery", sqlQuery);
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p>@Data.Text</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public Component Data { get; set; }
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
@page "/test"
|
||||
|
||||
@using System.Net;
|
||||
@using PanoptesFrontend.Data
|
||||
@using PanoptesFrontend.Services
|
||||
@using System.Text.Json;
|
||||
@inject IHttpService httpService
|
||||
|
||||
@if (data != null)
|
||||
{
|
||||
@foreach (var component in data) {
|
||||
switch (component.Type) {
|
||||
case 0:
|
||||
<TestCard Data="@component" />
|
||||
break;
|
||||
case 1:
|
||||
<ApexChart TItem="Stats"
|
||||
Title="Order Gross Value">
|
||||
|
||||
<ApexPointSeries TItem="Stats"
|
||||
Items=component.Stats
|
||||
SeriesType="SeriesType.Donut"
|
||||
Name="Gross Value"
|
||||
XValue="@(e => e.Label)"
|
||||
YValue="@(e => e.Value)"
|
||||
OrderByDescending="e=>e.Y" />
|
||||
</ApexChart>
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
private Component[] data;
|
||||
|
||||
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var response = await httpService.GetAsync<Schema>("http://localhost:10000/localhost:8080/schema");
|
||||
data = response.components;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,5 +7,7 @@
|
||||
|
||||
<script src="_content/Blazor-ApexCharts/js/apex-charts.min.js"></script>
|
||||
<script src="_content/Blazor-ApexCharts/js/blazor-apex-charts.js"></script>
|
||||
<link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
|
||||
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
|
||||
|
||||
<component type="typeof(App)" render-mode="ServerPrerendered" />
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
<PackageReference Include="Bunit" Version="1.19.14" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
<PackageReference Include="Radzen.Blazor" Version="4.10.3" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
@ -9,6 +9,7 @@ var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddServerSideBlazor();
|
||||
builder.Services.AddHttpClient();
|
||||
builder.Services.AddScoped<HttpClient>();
|
||||
builder.Services.AddScoped<IHttpService, HttpService>();
|
||||
builder.Services.AddScoped<IAccountService, AccountService>();
|
||||
builder.Services.AddBlazoredLocalStorage();
|
||||
|
||||
@ -9,7 +9,8 @@ public interface IAccountService
|
||||
{
|
||||
Task Register(AddUser model);
|
||||
Task Login(LoginUser model);
|
||||
Task Logout(User model);
|
||||
Task Logout(string token);
|
||||
Task<string> GetTokenFromLocalStorage();
|
||||
|
||||
}
|
||||
|
||||
@ -38,8 +39,16 @@ public class AccountService : IAccountService
|
||||
await localStorage.SetItemAsync("authToken", token);
|
||||
}
|
||||
|
||||
public async Task Logout(User model){
|
||||
await httpService.PostAsync("http://localhost:10000/user/logout", model);
|
||||
public async Task Logout(string token){
|
||||
|
||||
var authtoken = await localStorage.GetItemAsStringAsync("authToken");
|
||||
|
||||
await httpService.PostAsync("http://localhost:10000/user/logout", authtoken);
|
||||
await localStorage.RemoveItemAsync("authToken");
|
||||
}
|
||||
|
||||
public async Task<string> GetTokenFromLocalStorage()
|
||||
{
|
||||
return await localStorage.GetItemAsStringAsync("authToken");
|
||||
}
|
||||
}
|
||||
@ -15,8 +15,9 @@ public class HttpService : IHttpService {
|
||||
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
public HttpService(HttpClient httpClient){
|
||||
this.httpClient = httpClient;
|
||||
public HttpService(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
this.httpClient = httpClientFactory.CreateClient();
|
||||
}
|
||||
|
||||
public async Task<T> GetAsync<T>(string endpoint){
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
@inherits LayoutComponentBase
|
||||
@using PanoptesFrontend.Pages
|
||||
|
||||
<PageTitle>PanoptesFrontend</PageTitle>
|
||||
|
||||
@ -9,11 +10,12 @@
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
|
||||
<a href="account/login">Login</a>
|
||||
<Logout/>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">PanoptesFrontend</a>
|
||||
<a class="navbar-brand" href="">Panoptes</a>
|
||||
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
|
||||
<nav class="flex-column">
|
||||
@* @if (response != null)
|
||||
@if (response != null)
|
||||
{
|
||||
@foreach (var module in response){
|
||||
<div class="nav-item px-3">
|
||||
@ -23,18 +23,23 @@
|
||||
</NavLink>
|
||||
</div>
|
||||
}
|
||||
} *@
|
||||
}
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href=module-config Match="NavLinkMatch.All">
|
||||
<span class="oi oi-cog" aria-hidden="true"></span> Module config
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
@* public Module[] response;
|
||||
public Module[] response;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
response = await httpService.GetAsync<Module[]>("http://localhost:10000/modules");
|
||||
} *@
|
||||
}
|
||||
|
||||
private bool collapseNavMenu = true;
|
||||
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
|
||||
|
||||
@ -8,4 +8,6 @@
|
||||
@using Microsoft.JSInterop
|
||||
@using PanoptesFrontend
|
||||
@using PanoptesFrontend.Shared
|
||||
@using ApexCharts;
|
||||
@using ApexCharts
|
||||
@using Radzen
|
||||
@using Radzen.Blazor
|
||||
|
||||
@ -9,10 +9,10 @@ public class ModuleConfigTests : TestContext
|
||||
[Fact]
|
||||
public void NameInputFieldIsPresent()
|
||||
{
|
||||
Services.AddSingleton<IHttpService, HttpService>();
|
||||
// Arrange
|
||||
using var ctx = new TestContext();
|
||||
ctx.Services.AddSingleton<IHttpService, HttpService>();
|
||||
ctx.Services.AddHttpClient();
|
||||
var cut = ctx.RenderComponent<ModConfig>();
|
||||
|
||||
// Act
|
||||
@ -25,10 +25,10 @@ public class ModuleConfigTests : TestContext
|
||||
[Fact]
|
||||
public void ImageInputFieldIsPresent()
|
||||
{
|
||||
Services.AddSingleton<IHttpService, HttpService>();
|
||||
// Arrange
|
||||
using var ctx = new TestContext();
|
||||
ctx.Services.AddSingleton<IHttpService, HttpService>();
|
||||
ctx.Services.AddHttpClient();
|
||||
var cut = ctx.RenderComponent<ModConfig>();
|
||||
|
||||
// Act
|
||||
@ -41,10 +41,10 @@ public class ModuleConfigTests : TestContext
|
||||
[Fact]
|
||||
public void UserInputFieldIsPresent()
|
||||
{
|
||||
Services.AddSingleton<IHttpService, HttpService>();
|
||||
// Arrange
|
||||
using var ctx = new TestContext();
|
||||
ctx.Services.AddSingleton<IHttpService, HttpService>();
|
||||
ctx.Services.AddHttpClient();
|
||||
var cut = ctx.RenderComponent<ModConfig>();
|
||||
|
||||
// Act
|
||||
@ -57,10 +57,10 @@ public class ModuleConfigTests : TestContext
|
||||
[Fact]
|
||||
public void InternalInputFieldIsPresent()
|
||||
{
|
||||
Services.AddSingleton<IHttpService, HttpService>();
|
||||
// Arrange
|
||||
using var ctx = new TestContext();
|
||||
ctx.Services.AddSingleton<IHttpService, HttpService>();
|
||||
ctx.Services.AddHttpClient();
|
||||
var cut = ctx.RenderComponent<ModConfig>();
|
||||
|
||||
// Act
|
||||
@ -73,10 +73,12 @@ public class ModuleConfigTests : TestContext
|
||||
[Fact]
|
||||
public void EnvironmentVariableFormIsPresent()
|
||||
{
|
||||
Services.AddSingleton<IHttpService, HttpService>();
|
||||
|
||||
|
||||
// Arrange
|
||||
using var ctx = new TestContext();
|
||||
ctx.Services.AddSingleton<IHttpService, HttpService>();
|
||||
ctx.Services.AddHttpClient();
|
||||
var cut = ctx.RenderComponent<ModConfig>();
|
||||
|
||||
// Act
|
||||
@ -91,10 +93,11 @@ public class ModuleConfigTests : TestContext
|
||||
[Fact]
|
||||
public void VolumeFormIsPresent()
|
||||
{
|
||||
Services.AddSingleton<IHttpService, HttpService>();
|
||||
|
||||
// Arrange
|
||||
using var ctx = new TestContext();
|
||||
ctx.Services.AddSingleton<IHttpService, HttpService>();
|
||||
ctx.Services.AddHttpClient();
|
||||
var cut = ctx.RenderComponent<ModConfig>();
|
||||
|
||||
// Act
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p>
|
||||
Current count: @currentCount
|
||||
</p>
|
||||
|
||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||
|
||||
@code {
|
||||
int currentCount = 0;
|
||||
|
||||
void IncrementCount()
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
namespace PanoptesTest;
|
||||
|
||||
/// <summary>
|
||||
/// These tests are written entirely in C#.
|
||||
/// Learn more at https://bunit.dev/docs/getting-started/writing-tests.html#creating-basic-tests-in-cs-files
|
||||
/// </summary>
|
||||
public class CounterCSharpTests : TestContext
|
||||
{
|
||||
[Fact]
|
||||
public void CounterStartsAtZero()
|
||||
{
|
||||
// Arrange
|
||||
var cut = RenderComponent<Counter>();
|
||||
|
||||
// Assert that content of the paragraph shows counter at zero
|
||||
cut.Find("p").MarkupMatches("<p>Current count: 0</p>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClickingButtonIncrementsCounter()
|
||||
{
|
||||
// Arrange
|
||||
var cut = RenderComponent<Counter>();
|
||||
|
||||
// Act - click button to increment counter
|
||||
cut.Find("button").Click();
|
||||
|
||||
// Assert that the counter was incremented
|
||||
cut.Find("p").MarkupMatches("<p>Current count: 1</p>");
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
@inherits TestContext
|
||||
|
||||
These tests are written entirely in razor and C# syntax.
|
||||
|
||||
Learn more at https://bunit.dev/docs/getting-started/writing-tests.html#creating-basic-tests-in-razor-files
|
||||
|
||||
@code {
|
||||
[Fact]
|
||||
public void CounterStartsAtZero()
|
||||
{
|
||||
// Arrange
|
||||
var cut = Render(@<Counter />);
|
||||
|
||||
// Assert that content of the paragraph shows counter at zero
|
||||
cut.Find("p").MarkupMatches(@<p>Current count: 0</p>);
|
||||
}
|
||||
[Fact]
|
||||
public void ClickingButtonIncrementsCounter()
|
||||
{
|
||||
// Arrange
|
||||
var cut = Render(@<Counter />);
|
||||
|
||||
// Act - click button to increment counter
|
||||
cut.Find("button").Click();
|
||||
|
||||
// Assert that the counter was incremented
|
||||
cut.Find("p").MarkupMatches(@<p>Current count: 1</p>);
|
||||
}
|
||||
}
|
||||
102
src/PanoptesTest/DynamicChartTests.cs
Normal file
102
src/PanoptesTest/DynamicChartTests.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using PanoptesFrontend.Data;
|
||||
using PanoptesFrontend.Pages;
|
||||
using System.Collections.Generic;
|
||||
using PanoptesFrontend.Services;
|
||||
using ApexCharts;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
|
||||
|
||||
public class DynamicChartTests
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void DynamicChartRendersChartWithCorrectType()
|
||||
{
|
||||
// Arrange
|
||||
var testContext = new TestContext();
|
||||
testContext.Services.AddSingleton<IHttpService, HttpService>();
|
||||
testContext.Services.AddHttpClient();
|
||||
testContext.JSInterop.SetupVoid("blazor_apexchart.renderChart", _ => true);
|
||||
|
||||
var chartTitle = "Example Chart";
|
||||
var statsList = new List<Stats>
|
||||
{
|
||||
new Stats { Label = "A", Value = 1 },
|
||||
new Stats { Label = "B", Value = 2 },
|
||||
new Stats { Label = "C", Value = 3 }
|
||||
};
|
||||
var cut = testContext.RenderComponent<DynamicChart>(
|
||||
("ChartTitle", chartTitle),
|
||||
("StatsList", statsList),
|
||||
("SelectedChartType", "bar")
|
||||
);
|
||||
|
||||
// act
|
||||
cut.Instance.SelectedChartType = "line";
|
||||
var actualChartType = cut.Instance.SelectedChartType;
|
||||
|
||||
// assert
|
||||
var expectedChartType = "line";
|
||||
Assert.Equal(expectedChartType, actualChartType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DynamicChartRendersChartWithCorrectTitle()
|
||||
{
|
||||
// Arrange
|
||||
var testContext = new TestContext();
|
||||
testContext.Services.AddSingleton<IHttpService, HttpService>();
|
||||
testContext.Services.AddHttpClient();
|
||||
testContext.JSInterop.SetupVoid("blazor_apexchart.renderChart", _ => true);
|
||||
|
||||
var chartTitle = "Example Chart";
|
||||
var statsList = new List<Stats>
|
||||
{
|
||||
new Stats { Label = "A", Value = 1 },
|
||||
new Stats { Label = "B", Value = 2 },
|
||||
new Stats { Label = "C", Value = 3 }
|
||||
};
|
||||
var cut = testContext.RenderComponent<DynamicChart>(
|
||||
("ChartTitle", chartTitle),
|
||||
("StatsList", statsList),
|
||||
("SelectedChartType", "line")
|
||||
);
|
||||
// Act
|
||||
var actualTitle = cut.Instance.ChartTitle;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(chartTitle, actualTitle);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DynamicChartRendersWithStatsList()
|
||||
{
|
||||
// Arrange
|
||||
var testContext = new TestContext();
|
||||
testContext.Services.AddSingleton<IHttpService, HttpService>();
|
||||
testContext.Services.AddHttpClient();
|
||||
testContext.JSInterop.SetupVoid("blazor_apexchart.renderChart", _ => true);
|
||||
|
||||
var statsList = new List<Stats>
|
||||
{
|
||||
new Stats { Label = "Jan", Value = 100 },
|
||||
new Stats { Label = "Feb", Value = 200 },
|
||||
new Stats { Label = "Mar", Value = 150 }
|
||||
};
|
||||
var chartTitle = "My Chart";
|
||||
var selectedChartType = "line";
|
||||
|
||||
// Act
|
||||
var cut = testContext.RenderComponent<DynamicChart>(
|
||||
("StatsList", statsList),
|
||||
("ChartTitle", chartTitle),
|
||||
("SelectedChartType", selectedChartType)
|
||||
);
|
||||
var chart = cut.FindComponent<ApexChart<Stats>>();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(chart);
|
||||
Assert.Equal(statsList, cut.Instance.StatsList);
|
||||
}
|
||||
}
|
||||
40
src/PanoptesTest/DynamicTableTests.cs
Normal file
40
src/PanoptesTest/DynamicTableTests.cs
Normal file
@ -0,0 +1,40 @@
|
||||
using PanoptesFrontend.Pages;
|
||||
using System.Collections.Generic;
|
||||
using PanoptesFrontend.Services;
|
||||
using Radzen;
|
||||
using Radzen.Blazor;
|
||||
|
||||
public class DynamicGridTests
|
||||
{
|
||||
[Fact]
|
||||
public void DynamicGrid_Component_Initialization()
|
||||
{
|
||||
// Arrange
|
||||
using var ctx = new TestContext();
|
||||
ctx.Services.AddSingleton<IHttpService, HttpService>();
|
||||
ctx.Services.AddHttpClient();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var data = new List<Dictionary<string, object>> {
|
||||
new Dictionary<string, object> {
|
||||
{ "Column1", 1 },
|
||||
{ "Column2", "Value" }
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var cut = ctx.RenderComponent<DynamicTable>(parameters => parameters
|
||||
.Add(p => p.data, data));
|
||||
|
||||
cut.SetParametersAndRender(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.data, data);
|
||||
});
|
||||
|
||||
// Assert
|
||||
var grid = cut.FindComponent<RadzenDataGrid<IDictionary<string, object>>>();
|
||||
Assert.NotNull(grid);
|
||||
Assert.NotNull(grid.Instance);
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
|
||||
<PackageReference Include="Radzen.Blazor" Version="4.10.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user