diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Notifications/NotificationReport.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Notifications/NotificationReport.razor
index b93435a6..56d415cd 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Notifications/NotificationReport.razor
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Notifications/NotificationReport.razor
@@ -204,7 +204,8 @@
HasNextPage="@(_notifications.Count >= _pageSize)"
TotalCount="_totalCount"
PageSize="_pageSize"
- Disabled="_loading" />
+ Disabled="_loading"
+ HideWhenSinglePage="true" />
}
@@ -326,7 +327,7 @@
@* Email bodies are plain text (design: BCC delivery, plain text).
Rendered as preformatted text — never as a MarkupString, which
would be an XSS vector. *@
-
@_detail.Body
}
diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/OffsetPager.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/OffsetPager.razor
index 6cbdf4b8..118b43bc 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/OffsetPager.razor
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/OffsetPager.razor
@@ -2,8 +2,11 @@
@* Reusable offset-pagination bar. Purely presentational: the host page
owns page-number state, query execution, and the HasNextPage decision.
- Parameters: Page, PageChanged, HasNextPage, TotalCount, PageSize, Disabled. *@
+ Parameters: Page, PageChanged, HasNextPage, TotalCount, PageSize, Disabled,
+ HideWhenSinglePage. *@
+@if (!IsSinglePageAndHidden)
+{
+}
@code {
[Parameter] public int Page { get; set; } = 1;
@@ -30,8 +34,25 @@
[Parameter] public int PageSize { get; set; }
[Parameter] public bool Disabled { get; set; }
+ ///
+ /// When true, the whole bar is suppressed once the result set is
+ /// provably a single page — the conventional behaviour for a pager whose only
+ /// controls would be permanently disabled. Opt-in (default false)
+ /// because the summary span doubles as the "N total" result-count readout on
+ /// pages that have no other place to show it; a host that relies on that
+ /// readout leaves this off.
+ ///
+ [Parameter] public bool HideWhenSinglePage { get; set; }
+
private bool HasPrev => Page > 1;
+ // Hide only when single-page-ness is POSITIVELY established: a known page
+ // count of 0/1, sitting on page 1, with no next page. A null TotalCount
+ // (host cannot count) or any disagreement between the signals renders the
+ // bar, so the controls never vanish while a further page still exists.
+ private bool IsSinglePageAndHidden =>
+ HideWhenSinglePage && PageCount is <= 1 && Page <= 1 && !HasNextPage;
+
private int? PageCount =>
TotalCount is { } t && PageSize > 0
? (int)Math.Ceiling(t / (double)PageSize)
diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Shared/OffsetPagerTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Shared/OffsetPagerTests.cs
index 4fe7d9cc..237681b2 100644
--- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Shared/OffsetPagerTests.cs
+++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Shared/OffsetPagerTests.cs
@@ -90,6 +90,85 @@ public class OffsetPagerTests : BunitContext
Assert.Contains("230 total", summary.TextContent);
}
+ // ── HideWhenSinglePage_HidesBar_OnSinglePage ──────────────────────────────
+
+ [Fact]
+ public void HideWhenSinglePage_HidesBar_OnSinglePage()
+ {
+ var cut = Render(ps => ps
+ .Add(p => p.Page, 1)
+ .Add(p => p.PageSize, 50)
+ .Add(p => p.TotalCount, 12)
+ .Add(p => p.HasNextPage, false)
+ .Add(p => p.HideWhenSinglePage, true));
+
+ Assert.Empty(cut.FindAll("[data-test='pager-prev']"));
+ Assert.Empty(cut.FindAll("[data-test='pager-next']"));
+ Assert.Empty(cut.FindAll("[data-test='pager-summary']"));
+ }
+
+ // ── HideWhenSinglePage_HidesBar_OnEmptyResult ─────────────────────────────
+
+ [Fact]
+ public void HideWhenSinglePage_HidesBar_OnEmptyResult()
+ {
+ var cut = Render(ps => ps
+ .Add(p => p.Page, 1)
+ .Add(p => p.PageSize, 50)
+ .Add(p => p.TotalCount, 0)
+ .Add(p => p.HasNextPage, false)
+ .Add(p => p.HideWhenSinglePage, true));
+
+ Assert.Empty(cut.FindAll("[data-test='pager-summary']"));
+ }
+
+ // ── HideWhenSinglePage_ShowsBar_WhenMultiplePages ─────────────────────────
+
+ [Fact]
+ public void HideWhenSinglePage_ShowsBar_WhenMultiplePages()
+ {
+ var cut = Render(ps => ps
+ .Add(p => p.Page, 1)
+ .Add(p => p.PageSize, 50)
+ .Add(p => p.TotalCount, 120)
+ .Add(p => p.HasNextPage, true)
+ .Add(p => p.HideWhenSinglePage, true));
+
+ Assert.Contains("Page 1 of 3", cut.Find("[data-test='pager-summary']").TextContent);
+ }
+
+ // ── HideWhenSinglePage_ShowsBar_WhenTotalCountUnknown ─────────────────────
+ // TotalCount is null (the host cannot count), so single-page-ness is not
+ // provable — the bar must stay so the user can still reach a further page.
+
+ [Fact]
+ public void HideWhenSinglePage_ShowsBar_WhenTotalCountUnknown()
+ {
+ var cut = Render(ps => ps
+ .Add(p => p.Page, 1)
+ .Add(p => p.PageSize, 50)
+ .Add(p => p.HasNextPage, false)
+ .Add(p => p.HideWhenSinglePage, true));
+
+ Assert.NotNull(cut.Find("[data-test='pager-summary']"));
+ }
+
+ // ── HideWhenSinglePage_Off_KeepsBarVisible_OnSinglePage ───────────────────
+ // Default (opt-out) behaviour: consumers that use the summary as their only
+ // "N total" readout keep the bar on a single page.
+
+ [Fact]
+ public void HideWhenSinglePage_Off_KeepsBarVisible_OnSinglePage()
+ {
+ var cut = Render(ps => ps
+ .Add(p => p.Page, 1)
+ .Add(p => p.PageSize, 50)
+ .Add(p => p.TotalCount, 3)
+ .Add(p => p.HasNextPage, false));
+
+ Assert.Contains("3 total", cut.Find("[data-test='pager-summary']").TextContent);
+ }
+
// ── BothDisabled_WhenDisabled ─────────────────────────────────────────────
[Fact]