fix(centralui): stabilize audit grid th nodes with @key; doc grid limitations
This commit is contained in:
@@ -17,7 +17,13 @@
|
||||
<tr>
|
||||
@foreach (var col in OrderedColumns())
|
||||
{
|
||||
// @key keeps Blazor reusing one DOM node per column across
|
||||
// re-renders (reorder/resize), so audit-grid.js binds drag
|
||||
// listeners exactly once per <th> and never leaks them onto
|
||||
// discarded nodes — the __auditGridCellBound guard relies on
|
||||
// this node stability to be fully sound.
|
||||
<th class="audit-grid-th"
|
||||
@key="col.Key"
|
||||
data-test="col-header-@col.Key"
|
||||
data-col-key="@col.Key"
|
||||
style="@ColumnWidthStyle(col.Key)">
|
||||
|
||||
@@ -37,6 +37,16 @@ namespace ScadaLink.CentralUI.Components.Audit;
|
||||
/// <see cref="PageSize"/> rows) — that's the conventional "we've reached the
|
||||
/// end" signal for keyset paging without a count query.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Accessibility.</b> Column resize and reorder are mouse/pointer-only —
|
||||
/// they use a pointer-driven resize handle and native HTML5 drag-and-drop with
|
||||
/// no keyboard equivalent and no ARIA for the reorder. This is a conscious
|
||||
/// scope decision for an internal tool, not an oversight: only the column-
|
||||
/// <i>customisation</i> gesture is mouse-only. The persisted layout itself
|
||||
/// renders as plain HTML, so keyboard and assistive-technology users still get
|
||||
/// a fully readable, navigable grid.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public partial class AuditResultsGrid : IAsyncDisposable
|
||||
{
|
||||
@@ -99,6 +109,9 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
/// <c>data-test</c> + the column-order parameter); the label is the user-facing
|
||||
/// header text. Mirrors Component-AuditLog.md §10.
|
||||
/// </summary>
|
||||
// Label intentionally equals Key for every column today; the separate Label
|
||||
// field is future-proofing for humanised headers (e.g. "Occurred (UTC)") —
|
||||
// populating it is a deliberate later change, out of scope here.
|
||||
private static readonly IReadOnlyList<(string Key, string Label)> AllColumns = new[]
|
||||
{
|
||||
("OccurredAtUtc", "OccurredAtUtc"),
|
||||
@@ -251,6 +264,12 @@ public partial class AuditResultsGrid : IAsyncDisposable
|
||||
// is idempotent — already-bound cells are skipped, and the .NET
|
||||
// reference is refreshed — so a re-render after a reorder still leaves
|
||||
// every header cell wired without leaking handlers.
|
||||
//
|
||||
// OnColumnResized/OnColumnReordered both call StateHasChanged(), which
|
||||
// re-runs this method and calls init again. That repeat call is an
|
||||
// intentional cheap no-op: the @key-stable <th> nodes plus the
|
||||
// __auditGridCellBound guard mean init re-scans the header and rebinds
|
||||
// nothing — so there is deliberately no gating logic here.
|
||||
if (_selfRef is not null)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -171,6 +171,12 @@ window.auditGrid = {
|
||||
|
||||
// Apply a width to a <th> via a CSS custom property. The scoped stylesheet
|
||||
// reads --audit-col-width; absent it, the column falls back to auto.
|
||||
//
|
||||
// Known, intentional behaviour: during a live resize drag this updates the
|
||||
// <th> width immediately, but the <td> body cells only catch up on the next
|
||||
// .NET re-render (driven by OnColumnResized at pointer-up). The brief
|
||||
// header/body width mismatch mid-drag is an accepted trade-off for an
|
||||
// internal tool — not a bug.
|
||||
_applyWidth: function (th, widthPx) {
|
||||
th.style.setProperty("--audit-col-width", widthPx + "px");
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user