mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-11-30 02:58:37 +08:00
feat(Table): add RowTemplate (#3854)
* refactor: 精简代码 * feat: 增加 TableRowContext 上下文类 * refactor: 精简代码 * feat: 增加 RowTemplate 模板参数 * refactor: 增加 RowTemplate 模板逻辑 * doc: 增加 CustomTableRow 组件 * doc: 增加 RowTemplate 文档 * revert: 撤销代码写法 * test: 增加单元测试 * doc: 更新文档 * doc: 增加描述信息 * test: 更新单元测试 * refactor: 更新单元测试 * test: 更新单元测试 * test: 更新单元测试 * test: 更新单元测试
This commit is contained in:
parent
1d89e8fa57
commit
3b5274a315
@ -0,0 +1,6 @@
|
||||
<td>
|
||||
<DateTimePicker Value="@Context.Row.DateTime" OnValueChanged="OnDateTimeChanged"></DateTimePicker>
|
||||
</td>
|
||||
<td>@Context.Row.Name</td>
|
||||
<td>@Context.Row.Address</td>
|
||||
<td>@Context.Row.Count</td>
|
@ -0,0 +1,38 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Server.Components.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 自定义行组件
|
||||
/// </summary>
|
||||
public partial class CustomTableRow
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得/设置 行上下文数据实例
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[NotNull]
|
||||
public TableRowContext<Foo>? Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 值改变回调方法
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<TableRowContext<Foo>, Task>? OnValueChanged { get; set; }
|
||||
|
||||
private async Task OnDateTimeChanged(DateTime? dt)
|
||||
{
|
||||
// 通知数据源数据已更新
|
||||
Context.Row.DateTime = dt;
|
||||
Context.Row.Count = Random.Shared.Next(1, 100);
|
||||
if (OnValueChanged != null)
|
||||
{
|
||||
await OnValueChanged(Context);
|
||||
}
|
||||
|
||||
// 仅更新本组件不更新父 Table 组件
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
@ -142,3 +142,23 @@
|
||||
</TableColumns>
|
||||
</Table>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="@Localizer["RowTemplateTitle"]"
|
||||
Introduction="@Localizer["RowTemplateIntro"]"
|
||||
Name="RowTemplate">
|
||||
<section ignore>@((MarkupString)Localizer["RowTemplateDesc"].Value)</section>
|
||||
|
||||
<Table TItem="Foo" IsPagination="true" PageItemsSource="@PageItemsSource"
|
||||
IsStriped="true" IsBordered="true"
|
||||
OnQueryAsync="@OnQueryAsync">
|
||||
<TableColumns>
|
||||
<TableColumn @bind-Field="@context.DateTime" Width="180" />
|
||||
<TableColumn @bind-Field="@context.Name" Width="100" />
|
||||
<TableColumn @bind-Field="@context.Address" />
|
||||
<TableColumn @bind-Field="@context.Count" />
|
||||
</TableColumns>
|
||||
<RowTemplate>
|
||||
<CustomTableRow Context="@context" OnValueChanged="UpdateRowValue"></CustomTableRow>
|
||||
</RowTemplate>
|
||||
</Table>
|
||||
</DemoBlock>
|
||||
|
@ -98,4 +98,14 @@ public partial class TablesRow
|
||||
}
|
||||
|
||||
private static string? SetRowClassFormatter(Foo item) => item.Count > 60 ? "row-highlight" : null;
|
||||
|
||||
private Task UpdateRowValue(TableRowContext<Foo> context)
|
||||
{
|
||||
// 触发内部数据变化通知
|
||||
// 本例中 context.Row 就是数据源内数据,此处不需要进行数据处理
|
||||
// 如果使用数据库此处可以根据数据变化项进行数据库更新即可
|
||||
// 此处不需要 StateHasChanged 更新 UI
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
@ -5422,7 +5422,10 @@
|
||||
"RowNumberText": "Number",
|
||||
"ClickToSelectP3": "Currently selected row:",
|
||||
"ClickToSelectNoneText": "None",
|
||||
"PlaceHolder": "Cannot be empty, within 50 characters"
|
||||
"PlaceHolder": "Cannot be empty, within 50 characters",
|
||||
"RowTemplateTitle": "Row Template",
|
||||
"RowTemplateIntro": "By setting the <code>RowTemplate</code> template, you can implement custom row cell linkage logic by sub-packaging components to achieve performance optimization and avoid the problem of refreshing the entire table component after linkage due to cell data refresh.",
|
||||
"RowTemplateDesc": "In this example, a custom component is used to select a date, link the quantity column, randomly generate a random number, and save it to the original data, so there is no need to refresh the entire <code>Table</code> component."
|
||||
},
|
||||
"BootstrapBlazor.Server.Components.Samples.Table.TablesDynamic": {
|
||||
"TablesDynamicTitle": "Table Dynamic",
|
||||
|
@ -5422,7 +5422,10 @@
|
||||
"RowNumberText": "序号",
|
||||
"ClickToSelectP3": "当前选中行:",
|
||||
"ClickToSelectNoneText": "无",
|
||||
"PlaceHolder": "不可为空,50字以内"
|
||||
"PlaceHolder": "不可为空,50字以内",
|
||||
"RowTemplateTitle": "行模板",
|
||||
"RowTemplateIntro": "通过设置 <code>RowTemplate</code> 模板,可通过分装组件的方式实现自定义行内单元格联动逻辑,达到性能最优化,避免单元格数据刷新导致联动后需要刷新整个表格组件的问题,可用于销售类软件,调整单价时总价列变化需求",
|
||||
"RowTemplateDesc": "本例中通过自定义组件,实现选择日期后,联动数量列,随机生成一个随机数字,并且保存到原始数据中,从而不需要刷新整个 <code>Table</code> 组件"
|
||||
},
|
||||
"BootstrapBlazor.Server.Components.Samples.Table.TablesDynamic": {
|
||||
"TablesDynamicTitle": "Table 表格",
|
||||
|
@ -249,38 +249,46 @@
|
||||
{
|
||||
@RenderExtendButtons(item)
|
||||
}
|
||||
@foreach (var col in GetVisibleColumns())
|
||||
@if (RowTemplate != null)
|
||||
{
|
||||
var cellClass = "";
|
||||
string? value = null;
|
||||
RenderFragment? valueTemplate = null;
|
||||
<div class="table-cell">
|
||||
<label>
|
||||
@col.GetDisplayName()
|
||||
</label>
|
||||
@if (col.OnCellRender != null)
|
||||
{
|
||||
var cell = new TableCellArgs { Row = item, ColumnName = col.GetFieldName() };
|
||||
col.OnCellRender(cell);
|
||||
cellClass = cell.Class;
|
||||
value = cell.Value;
|
||||
valueTemplate = cell.ValueTemplate;
|
||||
}
|
||||
<span class="@cellClass">
|
||||
@if (valueTemplate != null)
|
||||
var columns = GetVisibleColumns();
|
||||
@RowTemplate(new (item, columns));
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var col in GetVisibleColumns())
|
||||
{
|
||||
var cellClass = "";
|
||||
string? value = null;
|
||||
RenderFragment? valueTemplate = null;
|
||||
<div class="table-cell">
|
||||
<label>
|
||||
@col.GetDisplayName()
|
||||
</label>
|
||||
@if (col.OnCellRender != null)
|
||||
{
|
||||
@valueTemplate
|
||||
var cell = new TableCellArgs { Row = item, ColumnName = col.GetFieldName() };
|
||||
col.OnCellRender(cell);
|
||||
cellClass = cell.Class;
|
||||
value = cell.Value;
|
||||
valueTemplate = cell.ValueTemplate;
|
||||
}
|
||||
else if (value != null)
|
||||
{
|
||||
@value
|
||||
}
|
||||
else
|
||||
{
|
||||
@GetValue(col, item)
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<span class="@cellClass">
|
||||
@if (valueTemplate != null)
|
||||
{
|
||||
@valueTemplate
|
||||
}
|
||||
else if (value != null)
|
||||
{
|
||||
@value
|
||||
}
|
||||
else
|
||||
{
|
||||
@GetValue(col, item)
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (ShowExtendButtons && !IsExtendButtonsInRowHeader)
|
||||
{
|
||||
@ -656,14 +664,15 @@
|
||||
|
||||
RenderFragment<TItem> RenderRow => item =>
|
||||
@<DynamicElement TagName="tr" class="@GetRowClassString(item)"
|
||||
TriggerContextMenu="ContextMenuZone != null" OnContextMenu="e => OnContextMenu(e, item)" @ontouchstart="e => OnTouchStart(e, item)" @ontouchend="OnTouchEnd"
|
||||
TriggerContextMenu="ContextMenuZone != null" OnContextMenu="e => OnContextMenu(e, item)"
|
||||
@ontouchstart="e => OnTouchStart(e, item)" @ontouchend="OnTouchEnd"
|
||||
TriggerClick="@(ClickToSelect || OnClickRowCallback != null)" OnClick="() => ClickRow(item)"
|
||||
TriggerDoubleClick="@(DoubleClickToEdit || OnDoubleClickRowCallback != null)" OnDoubleClick="() => DoubleClickRow(item)">
|
||||
@if (ShowDetails())
|
||||
{
|
||||
<td class="@DetailColumnClassString" style="@DetailColumnStyleString">
|
||||
<div class="@GetDetailBarClassString(item)">
|
||||
@if (ShowDetailRow == null || ShowDetailRow.Invoke(item))
|
||||
@if (ShowDetailRow == null || ShowDetailRow(item))
|
||||
{
|
||||
<i class="@GetDetailCaretClassString(item)" @onclick:stopPropagation @onclick="() => ExpandDetailRow(item)"></i>
|
||||
}
|
||||
@ -696,88 +705,96 @@
|
||||
{
|
||||
@RenderRowExtendButtons(item)
|
||||
}
|
||||
@foreach (var col in GetVisibleColumns())
|
||||
@if (RowTemplate != null)
|
||||
{
|
||||
if (CheckShownWithBreakpoint(col))
|
||||
var columns = GetVisibleColumns();
|
||||
@RowTemplate(new (item, columns));
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var col in GetVisibleColumns())
|
||||
{
|
||||
if (colIndex > 1)
|
||||
if (CheckShownWithBreakpoint(col))
|
||||
{
|
||||
colIndex--;
|
||||
continue;
|
||||
}
|
||||
var cellClass = "";
|
||||
var colspan = 0;
|
||||
string? value = null;
|
||||
RenderFragment? valueTemplate = null;
|
||||
if (col.OnCellRender != null)
|
||||
{
|
||||
var cell = new TableCellArgs { Row = item, ColumnName = col.GetFieldName() };
|
||||
col.OnCellRender(cell);
|
||||
cellClass = cell.Class;
|
||||
colspan = cell.Colspan;
|
||||
valueTemplate = cell.ValueTemplate;
|
||||
value = cell.Value;
|
||||
colIndex = colspan;
|
||||
}
|
||||
<td colspan="@GetColSpan(colspan)" class="@GetFixedCellClassString(col, cellClass)" style="@GetFixedCellStyleString(col)">
|
||||
@{
|
||||
var isFirstColOfTree = IsTree && index++ == 0;
|
||||
var degree = 0;
|
||||
var isExpend = false;
|
||||
var hasChildren = false;
|
||||
if (isFirstColOfTree)
|
||||
{
|
||||
var treeItem = TreeNodeCache.Find(TreeRows, item, out degree);
|
||||
if(treeItem != null)
|
||||
{
|
||||
isExpend = treeItem.IsExpand;
|
||||
hasChildren = treeItem.HasChildren;
|
||||
}
|
||||
}
|
||||
var hesTreeChildren = isFirstColOfTree && hasChildren;
|
||||
if (colIndex > 1)
|
||||
{
|
||||
colIndex--;
|
||||
continue;
|
||||
}
|
||||
<DynamicElement TagName="div" TriggerClick="@hesTreeChildren"
|
||||
OnClick="@ToggleTreeRow(item)" StopPropagation="hesTreeChildren"
|
||||
class="@GetCellClassString(col, hesTreeChildren, isInCell)" style="@GetCellStyleString(col)">
|
||||
@if (isFirstColOfTree)
|
||||
{
|
||||
<div class="is-node" style="@GetTreeStyleString(degree)">
|
||||
@if (hesTreeChildren)
|
||||
var cellClass = "";
|
||||
var colspan = 0;
|
||||
string? value = null;
|
||||
RenderFragment? valueTemplate = null;
|
||||
if (col.OnCellRender != null)
|
||||
{
|
||||
var cell = new TableCellArgs { Row = item, ColumnName = col.GetFieldName() };
|
||||
col.OnCellRender(cell);
|
||||
cellClass = cell.Class;
|
||||
colspan = cell.Colspan;
|
||||
valueTemplate = cell.ValueTemplate;
|
||||
value = cell.Value;
|
||||
colIndex = colspan;
|
||||
}
|
||||
<td colspan="@GetColSpan(colspan)" class="@GetFixedCellClassString(col, cellClass)" style="@GetFixedCellStyleString(col)">
|
||||
@{
|
||||
var isFirstColOfTree = IsTree && index++ == 0;
|
||||
var degree = 0;
|
||||
var isExpend = false;
|
||||
var hasChildren = false;
|
||||
if (isFirstColOfTree)
|
||||
{
|
||||
<i class="@GetTreeClassString(isExpend)"></i>
|
||||
var treeItem = TreeNodeCache.Find(TreeRows, item, out degree);
|
||||
if(treeItem != null)
|
||||
{
|
||||
isExpend = treeItem.IsExpand;
|
||||
hasChildren = treeItem.HasChildren;
|
||||
}
|
||||
}
|
||||
</div>
|
||||
var hesTreeChildren = isFirstColOfTree && hasChildren;
|
||||
}
|
||||
@if (IsExcel)
|
||||
{
|
||||
@RenderExcelCell(col, item, ItemChangedType.Update)
|
||||
}
|
||||
else if(isInCell)
|
||||
{
|
||||
@RenderCell(col, EditModel, AddInCell ? ItemChangedType.Add : ItemChangedType.Update)
|
||||
}
|
||||
else
|
||||
{
|
||||
var triggerDoubleClick = OnDoubleClickCellCallback != null;
|
||||
<DynamicElement TagName="div" TriggerDoubleClick="triggerDoubleClick" GenerateElement="false"
|
||||
OnDoubleClick="TriggerDoubleClickCell(col, item)" StopPropagation="true"
|
||||
class="@GetDoubleClickCellClassString(triggerDoubleClick)">
|
||||
@if (valueTemplate != null)
|
||||
<DynamicElement TagName="div" TriggerClick="@hesTreeChildren"
|
||||
OnClick="@ToggleTreeRow(item)" StopPropagation="hesTreeChildren"
|
||||
class="@GetCellClassString(col, hesTreeChildren, isInCell)" style="@GetCellStyleString(col)">
|
||||
@if (isFirstColOfTree)
|
||||
{
|
||||
<div class="is-node" style="@GetTreeStyleString(degree)">
|
||||
@if (hesTreeChildren)
|
||||
{
|
||||
@valueTemplate
|
||||
<i class="@GetTreeClassString(isExpend)"></i>
|
||||
}
|
||||
else if (value != null)
|
||||
{
|
||||
@value
|
||||
}
|
||||
else
|
||||
{
|
||||
@GetValue(col, item)
|
||||
}
|
||||
</DynamicElement>
|
||||
}
|
||||
</DynamicElement>
|
||||
</td>
|
||||
</div>
|
||||
}
|
||||
@if (IsExcel)
|
||||
{
|
||||
@RenderExcelCell(col, item, ItemChangedType.Update)
|
||||
}
|
||||
else if(isInCell)
|
||||
{
|
||||
@RenderCell(col, EditModel, AddInCell ? ItemChangedType.Add : ItemChangedType.Update)
|
||||
}
|
||||
else
|
||||
{
|
||||
var triggerDoubleClick = OnDoubleClickCellCallback != null;
|
||||
<DynamicElement TagName="div" TriggerDoubleClick="triggerDoubleClick" GenerateElement="false"
|
||||
OnDoubleClick="TriggerDoubleClickCell(col, item)" StopPropagation="true"
|
||||
class="@GetDoubleClickCellClassString(triggerDoubleClick)">
|
||||
@if (valueTemplate != null)
|
||||
{
|
||||
@valueTemplate
|
||||
}
|
||||
else if (value != null)
|
||||
{
|
||||
@value
|
||||
}
|
||||
else
|
||||
{
|
||||
@GetValue(col, item)
|
||||
}
|
||||
</DynamicElement>
|
||||
}
|
||||
</DynamicElement>
|
||||
</td>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (ShowExtendButtons && !IsExtendButtonsInRowHeader)
|
||||
|
@ -450,6 +450,12 @@ public partial class Table<TItem> : ITable, IModelEqualityComparer<TItem> where
|
||||
[Parameter]
|
||||
public RenderFragment<TItem>? DetailRowTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 行模板
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public RenderFragment<TableRowContext<TItem>>? RowTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 TableHeader 实例
|
||||
/// </summary>
|
||||
|
@ -7,9 +7,6 @@ namespace BootstrapBlazor.Components;
|
||||
/// <summary>
|
||||
/// TableColumn 上下文类
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 构造方法
|
||||
/// </remarks>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="value"></param>
|
||||
public class TableColumnContext<TItem, TValue>(TItem model, TValue value)
|
||||
@ -23,5 +20,5 @@ public class TableColumnContext<TItem, TValue>(TItem model, TValue value)
|
||||
/// <summary>
|
||||
/// 获得/设置 当前绑定字段数据实例
|
||||
/// </summary>
|
||||
public TValue Value { get; } = value;
|
||||
public TValue Value => value;
|
||||
}
|
||||
|
24
src/BootstrapBlazor/Components/Table/TableRowContext.cs
Normal file
24
src/BootstrapBlazor/Components/Table/TableRowContext.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// TableRow 上下文类
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="columns"></param>
|
||||
public class TableRowContext<TItem>(TItem model, IEnumerable<ITableColumn> columns)
|
||||
{
|
||||
/// <summary>
|
||||
/// 获得/设置 行数据实例
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
public TItem Row { get; } = model ?? throw new ArgumentNullException(nameof(model));
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 当前绑定字段数据实例
|
||||
/// </summary>
|
||||
public IEnumerable<ITableColumn> Columns => columns;
|
||||
}
|
@ -150,10 +150,10 @@ public class AutoGenerateClassTest
|
||||
attrInterface.IsReadonlyWhenEdit = true;
|
||||
Assert.True(attrInterface.IsReadonlyWhenEdit);
|
||||
|
||||
attrInterface.GetTooltipTextCallback = new Func<object?, Task<string?>>(_ => Task.FromResult((string?)"Test"));
|
||||
attrInterface.GetTooltipTextCallback = _ => Task.FromResult((string?)"Test");
|
||||
Assert.NotNull(attrInterface.GetTooltipTextCallback);
|
||||
|
||||
attrInterface.CustomSearch = new Func<ITableColumn, string?, SearchFilterAction>((_, _) => new SearchFilterAction("test", "test"));
|
||||
attrInterface.CustomSearch = (_, _) => new SearchFilterAction("test", "test");
|
||||
Assert.NotNull(attrInterface.CustomSearch);
|
||||
|
||||
attrInterface.Searchable = null;
|
||||
|
@ -274,6 +274,7 @@ public class ButtonTest : BootstrapBlazorTestBase
|
||||
|
||||
await cut.InvokeAsync(() =>
|
||||
{
|
||||
var button = cut.FindComponent<Button>();
|
||||
button.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.TooltipText, "Tooltip-Button");
|
||||
|
@ -6,6 +6,7 @@ using AngleSharp.Dom;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace UnitTest.Components;
|
||||
|
||||
@ -132,7 +133,8 @@ public class ContextMenuTest : BootstrapBlazorTestBase
|
||||
TriggerTouchStart(row);
|
||||
TriggerTouchStart(row);
|
||||
|
||||
await Task.Delay(500);
|
||||
var options = Context.Services.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
|
||||
await Task.Delay(100 + options.Value.ContextMenuOptions.OnTouchDelay);
|
||||
row.TouchEnd();
|
||||
});
|
||||
}
|
||||
@ -197,7 +199,8 @@ public class ContextMenuTest : BootstrapBlazorTestBase
|
||||
TriggerTouchStart(row);
|
||||
TriggerTouchStart(row);
|
||||
|
||||
await Task.Delay(500);
|
||||
var options = Context.Services.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
|
||||
await Task.Delay(100 + options.Value.ContextMenuOptions.OnTouchDelay);
|
||||
row.TouchEnd();
|
||||
});
|
||||
}
|
||||
|
@ -44,6 +44,10 @@ public class TableColumnTest
|
||||
{
|
||||
builder.AddContent(0, col.GetFieldName());
|
||||
}));
|
||||
SetValue("ToolboxTemplate", new RenderFragment<ITableColumn>(col => builder =>
|
||||
{
|
||||
builder.AddContent(0, "test");
|
||||
}));
|
||||
SetValue("Filter", new TableFilter());
|
||||
SetValue("FormatString", "test");
|
||||
SetValue("Formatter", new Func<object?, Task<string>>(val =>
|
||||
|
@ -7492,6 +7492,12 @@ public class TableTest : TableTestBase
|
||||
Assert.Throws<ArgumentNullException>(() => new TableColumnContext<Foo?, string>(null, "Test-Value"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TableRowContext_Exception()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => new TableRowContext<Foo?>(null, [new InternalTableColumn("Name", typeof(string))]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlaceHolder_Ok()
|
||||
{
|
||||
@ -7984,6 +7990,43 @@ public class TableTest : TableTestBase
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TableRenderMode.Table)]
|
||||
[InlineData(TableRenderMode.CardView)]
|
||||
public void RowTemplate_Ok(TableRenderMode mode)
|
||||
{
|
||||
var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>();
|
||||
var items = Foo.GenerateFoo(localizer, 2);
|
||||
IEnumerable<ITableColumn>? columns = null;
|
||||
var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb =>
|
||||
{
|
||||
pb.AddChildContent<Table<Foo>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.RenderMode, mode);
|
||||
pb.Add(a => a.Items, items);
|
||||
pb.Add(a => a.TableColumns, foo => builder =>
|
||||
{
|
||||
builder.OpenComponent<TableColumn<Foo, string>>(0);
|
||||
builder.AddAttribute(1, "Field", "Name");
|
||||
builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string)));
|
||||
builder.CloseComponent();
|
||||
});
|
||||
pb.Add(a => a.RowTemplate, context => builder =>
|
||||
{
|
||||
builder.OpenElement(0, "div");
|
||||
builder.AddContent(1, $"template-{context.Row.Name}");
|
||||
builder.CloseElement();
|
||||
|
||||
columns = context.Columns;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Contains($"template-{items[0].Name}", cut.Markup);
|
||||
Assert.NotNull(columns);
|
||||
Assert.Single(columns);
|
||||
}
|
||||
|
||||
private static DataTable CreateDataTable(IStringLocalizer<Foo> localizer)
|
||||
{
|
||||
var userData = new DataTable();
|
||||
|
@ -47,7 +47,7 @@ public class ITableColumnExtensionsTest
|
||||
ComponentType = typeof(NullSwitch),
|
||||
ComponentParameters = [],
|
||||
Ignore = true,
|
||||
EditTemplate = new RenderFragment<object>(obj => builder => builder.AddContent(0, "test")),
|
||||
EditTemplate = obj => builder => builder.AddContent(0, "test"),
|
||||
Items = new List<SelectedItem>(),
|
||||
Lookup = new List<SelectedItem>(),
|
||||
LookupStringComparison = StringComparison.Ordinal,
|
||||
@ -72,7 +72,7 @@ public class ITableColumnExtensionsTest
|
||||
DefaultSortOrder = SortOrder.Desc,
|
||||
Filter = new TableFilter(),
|
||||
Filterable = true,
|
||||
FilterTemplate = new RenderFragment(builder => builder.AddContent(0, "test-filter")),
|
||||
FilterTemplate = builder => builder.AddContent(0, "test-filter"),
|
||||
Fixed = true,
|
||||
FormatString = "test-format",
|
||||
Formatter = obj =>
|
||||
@ -80,15 +80,15 @@ public class ITableColumnExtensionsTest
|
||||
var ret = "test-formatter";
|
||||
return Task.FromResult<string?>(ret);
|
||||
},
|
||||
HeaderTemplate = new RenderFragment<ITableColumn>(col => builder => builder.AddContent(0, "test-header")),
|
||||
ToolboxTemplate = new RenderFragment<ITableColumn>(col => builder => builder.AddContent(0, "test-toolbox")),
|
||||
HeaderTemplate = col => builder => builder.AddContent(0, "test-header"),
|
||||
ToolboxTemplate = col => builder => builder.AddContent(0, "test-toolbox"),
|
||||
OnCellRender = args => { },
|
||||
Searchable = true,
|
||||
SearchTemplate = new RenderFragment<object>(obj => builder => builder.AddContent(0, "test-search")),
|
||||
SearchTemplate = obj => builder => builder.AddContent(0, "test-search"),
|
||||
ShownWithBreakPoint = BreakPoint.Large,
|
||||
ShowTips = true,
|
||||
Sortable = true,
|
||||
Template = new RenderFragment<object>(obj => builder => builder.AddContent(0, "test-template")),
|
||||
Template = obj => builder => builder.AddContent(0, "test-template"),
|
||||
TextEllipsis = true,
|
||||
Visible = false,
|
||||
IsVisibleWhenAdd = false,
|
||||
@ -105,7 +105,7 @@ public class ITableColumnExtensionsTest
|
||||
Order = -1,
|
||||
IsMarkupString = true,
|
||||
GetTooltipTextCallback = _ => Task.FromResult<string?>(null),
|
||||
CustomSearch = new Func<ITableColumn, string?, SearchFilterAction>((_, _) => new SearchFilterAction("test", "test"))
|
||||
CustomSearch = (_, _) => new SearchFilterAction("test", "test")
|
||||
};
|
||||
col.CopyValue(attr);
|
||||
Assert.NotNull(col.ComponentType);
|
||||
|
Loading…
Reference in New Issue
Block a user