203 lines
7.4 KiB
C#
203 lines
7.4 KiB
C#
using AyCode.Blazor.Components.Components.Grids;
|
|
using AyCode.Core.Helpers;
|
|
using AyCode.Core.Interfaces;
|
|
using AyCode.Core.Loggers;
|
|
using AyCode.Core.Tests.TestModels;
|
|
using AyCode.Services.Server.SignalRs;
|
|
using AyCode.Services.Server.Tests.SignalRs;
|
|
using AyCode.Services.Server.Tests.SignalRs.SignalRDatasources;
|
|
using AyCode.Services.SignalRs;
|
|
using DevExpress.Blazor;
|
|
using Microsoft.AspNetCore.Components;
|
|
using System.Reflection;
|
|
using Microsoft.AspNetCore.Components.Rendering;
|
|
|
|
namespace AyCode.Blazor.Components.Tests.Grids;
|
|
|
|
/// <summary>
|
|
/// Test DataSource that extends TestOrderItemObservableDataSource with the 3-parameter constructor
|
|
/// required by MgGridBase.OnInitializedAsync which uses Activator.CreateInstance.
|
|
/// </summary>
|
|
public class TestGridOrderItemDataSource : TestOrderItemObservableDataSource
|
|
{
|
|
public TestGridOrderItemDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags)
|
|
: base(signalRClient, crudTags) { }
|
|
|
|
public TestGridOrderItemDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags, params object[]? contextIds)
|
|
: base(signalRClient, crudTags)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event args for dynamic column adding event.
|
|
/// Provides a delegate to add custom attributes to the column.
|
|
/// </summary>
|
|
public class DynamicColumnAddingEventArgs
|
|
{
|
|
public required string FieldName { get; init; }
|
|
public required PropertyInfo PropertyInfo { get; init; }
|
|
|
|
/// <summary>
|
|
/// Dictionary of additional attributes to add to the column.
|
|
/// Key is the attribute name, value is the attribute value.
|
|
/// </summary>
|
|
public Dictionary<string, object?> AdditionalAttributes { get; } = new();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base test implementation of MgGridBase for testing grid functionality.
|
|
/// Overrides layout persistence to use in-memory storage for testing.
|
|
/// Automatically builds columns from TDataItem properties using reflection.
|
|
/// </summary>
|
|
public abstract class TestMgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient>
|
|
: MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient>
|
|
where TSignalRDataSource : AcSignalRDataSource<TDataItem, TId, AcObservableCollection<TDataItem>>
|
|
where TDataItem : class, IId<TId>
|
|
where TId : struct
|
|
where TLoggerClient : AcLoggerBase
|
|
{
|
|
private int _testUserId;
|
|
private bool _columnsInitialized;
|
|
|
|
/// <summary>
|
|
/// In-memory storage for layout persistence testing.
|
|
/// Shared across all instances to simulate localStorage behavior.
|
|
/// </summary>
|
|
public static Dictionary<string, GridPersistentLayout?> LayoutStorage { get; } = new();
|
|
|
|
/// <summary>
|
|
/// Indicates whether data source has been loaded
|
|
/// </summary>
|
|
public bool IsDataSourceLoaded { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Event called when a dynamic column is being added. Allows customization of column properties.
|
|
/// Add attributes to eventArgs.AdditionalAttributes dictionary.
|
|
/// </summary>
|
|
[Parameter]
|
|
public Action<DynamicColumnAddingEventArgs>? OnDynamicColumnAttributeAdding { get; set; }
|
|
|
|
public void SetTestUserId(int userId) => _testUserId = userId;
|
|
public int GetTestUserId() => _testUserId;
|
|
protected override int GetLayoutUserId() => _testUserId;
|
|
|
|
public static void ClearLayoutStorage() => LayoutStorage.Clear();
|
|
|
|
protected override Task<GridPersistentLayout?> LoadLayoutFromLocalStorageAsync(string localStorageKey)
|
|
{
|
|
LayoutStorage.TryGetValue(localStorageKey, out var layout);
|
|
return Task.FromResult(layout);
|
|
}
|
|
|
|
protected override Task SaveLayoutToLocalStorageAsync(GridPersistentLayout layout, string localStorageKey)
|
|
{
|
|
LayoutStorage[localStorageKey] = layout;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Waits for the data source to be loaded using TaskHelper
|
|
/// </summary>
|
|
public Task<bool> WaitForDataSourceLoadedAsync(int timeoutMs = 5000)
|
|
{
|
|
return TaskHelper.WaitToAsync(() => IsDataSourceLoaded, timeoutMs);
|
|
}
|
|
|
|
protected override void OnParametersSet()
|
|
{
|
|
if (!_columnsInitialized)
|
|
{
|
|
// Build columns from TDataItem properties using reflection
|
|
Columns = BuildColumnsFromDataItem;
|
|
_columnsInitialized = true;
|
|
}
|
|
|
|
base.OnParametersSet();
|
|
|
|
// Subscribe to OnDataSourceChanged to know when data is loaded
|
|
OnDataSourceChanged = EventCallback.Factory.Create<IList<TDataItem>>(this, _ =>
|
|
{
|
|
IsDataSourceLoaded = true;
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds grid columns from TDataItem properties using reflection
|
|
/// </summary>
|
|
private void BuildColumnsFromDataItem(RenderTreeBuilder builder)
|
|
{
|
|
var properties = typeof(TDataItem).GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
|
.Where(p => p.CanRead && IsSimpleType(p.PropertyType))
|
|
.ToList();
|
|
|
|
var seq = 0;
|
|
foreach (var property in properties)
|
|
{
|
|
// Create event args and invoke the event
|
|
var eventArgs = new DynamicColumnAddingEventArgs
|
|
{
|
|
FieldName = property.Name,
|
|
PropertyInfo = property
|
|
};
|
|
OnDynamicColumnAttributeAdding?.Invoke(eventArgs);
|
|
|
|
builder.OpenComponent<MgGridDataColumn>(seq++);
|
|
builder.AddAttribute(seq++, nameof(MgGridDataColumn.Name), property.Name);
|
|
builder.AddAttribute(seq++, nameof(MgGridDataColumn.FieldName), property.Name);
|
|
builder.AddAttribute(seq++, nameof(MgGridDataColumn.Width), GetDefaultWidth(property.PropertyType));
|
|
|
|
// Add additional attributes from the event
|
|
foreach (var attr in eventArgs.AdditionalAttributes)
|
|
{
|
|
builder.AddAttribute(seq++, attr.Key, attr.Value);
|
|
}
|
|
|
|
builder.CloseComponent();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if a type is a simple type suitable for grid display
|
|
/// </summary>
|
|
private static bool IsSimpleType(Type type)
|
|
{
|
|
var underlyingType = Nullable.GetUnderlyingType(type) ?? type;
|
|
|
|
return underlyingType.IsPrimitive
|
|
|| underlyingType == typeof(string)
|
|
|| underlyingType == typeof(decimal)
|
|
|| underlyingType == typeof(DateTime)
|
|
|| underlyingType == typeof(DateTimeOffset)
|
|
|| underlyingType == typeof(TimeSpan)
|
|
|| underlyingType == typeof(Guid)
|
|
|| underlyingType.IsEnum;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets default column width based on property type
|
|
/// </summary>
|
|
private static string GetDefaultWidth(Type type)
|
|
{
|
|
var underlyingType = Nullable.GetUnderlyingType(type) ?? type;
|
|
|
|
return underlyingType switch
|
|
{
|
|
_ when underlyingType == typeof(int) || underlyingType == typeof(long) => "80px",
|
|
_ when underlyingType == typeof(decimal) || underlyingType == typeof(double) || underlyingType == typeof(float) => "100px",
|
|
_ when underlyingType == typeof(bool) => "60px",
|
|
_ when underlyingType == typeof(DateTime) || underlyingType == typeof(DateTimeOffset) => "150px",
|
|
_ when underlyingType == typeof(string) => "200px",
|
|
_ when underlyingType == typeof(Guid) => "250px",
|
|
_ => "120px"
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test grid for TestOrderItem entities
|
|
/// </summary>
|
|
public class TestMgGridOrderItem : TestMgGridBase<TestGridOrderItemDataSource, TestOrderItem, int, TestLogger>
|
|
{
|
|
}
|