refactor(ui): unify semantic presentation and table hierarchy
This commit is contained in:
@@ -23,6 +23,39 @@ public class DataTablePagerTests : BunitContext
|
||||
.Add(p => p.RowContent, (RenderFragment<int>)(item => b => b.AddMarkupContent(0, $"<tr><td>{item}</td></tr>"))));
|
||||
}
|
||||
|
||||
/// <summary>True empty results show no contradictory one-to-zero range.</summary>
|
||||
[Fact]
|
||||
public void EmptyTable_ShowsZeroItems()
|
||||
{
|
||||
var cut = RenderTable(0);
|
||||
Assert.Equal("0 items", cut.Find(".text-muted.small").TextContent.Trim());
|
||||
}
|
||||
|
||||
/// <summary>Filtering to zero and clearing preserves the correct range and paging state.</summary>
|
||||
[Fact]
|
||||
public void FilterToEmpty_ThenClear_RestoresRealCount()
|
||||
{
|
||||
var cut = RenderTable(60);
|
||||
cut.Render(p => p.Add(c => c.ShowSearch, true).Add(c => c.SearchFilter, (item, search) => item.ToString().Contains(search)));
|
||||
cut.Find("input").Input("no-match");
|
||||
Assert.Equal("0 items", cut.Find(".text-muted.small").TextContent.Trim());
|
||||
cut.Find("button[aria-label='Clear search']").Click();
|
||||
Assert.Contains("1–25 of 60 items", cut.Find(".text-muted.small").TextContent);
|
||||
}
|
||||
|
||||
/// <summary>The reusable table has one named keyboard-accessible overflow region.</summary>
|
||||
[Fact]
|
||||
public void Table_HasNamedKeyboardScrollRegion()
|
||||
{
|
||||
var cut = RenderTable(4);
|
||||
var region = cut.Find("[role=region]");
|
||||
Assert.Equal("Data table", region.GetAttribute("aria-label"));
|
||||
Assert.Equal("0", region.GetAttribute("tabindex"));
|
||||
Assert.Single(region.QuerySelectorAll("table"));
|
||||
cut.Render(p => p.Add(c => c.TableLabel, "Synthetic records"));
|
||||
Assert.Equal("Synthetic records", cut.Find("[role=region]").GetAttribute("aria-label"));
|
||||
}
|
||||
|
||||
private static int NumberedPageButtons(IRenderedComponent<DataTable<int>> cut)
|
||||
=> cut.FindAll("ul.pagination li.page-item button")
|
||||
.Count(b => int.TryParse(b.TextContent.Trim(), out _));
|
||||
|
||||
@@ -150,6 +150,26 @@ public class TopologyPageTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Every load state keeps one semantic page title.</summary>
|
||||
[Theory]
|
||||
[InlineData("loading")]
|
||||
[InlineData("loaded")]
|
||||
[InlineData("error")]
|
||||
public async Task PageTitle_RemainsOneH1AcrossLoadStates(string state)
|
||||
{
|
||||
SeedRepos();
|
||||
var pending = new TaskCompletionSource<IReadOnlyList<Site>>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
if (state == "loading") _siteRepo.GetAllSitesAsync().Returns(pending.Task);
|
||||
if (state == "error") _siteRepo.GetAllSitesAsync().Returns(Task.FromException<IReadOnlyList<Site>>(new Exception("local failure")));
|
||||
var cut = Render<TopologyPage>();
|
||||
try
|
||||
{
|
||||
Assert.Single(cut.FindAll("h1"));
|
||||
Assert.Equal("Topology", cut.Find("h1").TextContent.Trim());
|
||||
}
|
||||
finally { await cut.InvokeAsync(() => pending.TrySetResult([])); }
|
||||
}
|
||||
|
||||
private IRenderedComponent<TopologyPage> RenderLoadedTopology()
|
||||
{
|
||||
var cut = Render<TopologyPage>();
|
||||
|
||||
Reference in New Issue
Block a user