Skip to content

fix(ui): derive the live containers from the DOM, not from rosters - #204

Merged
argszero merged 1 commit into
mainfrom
fix/ui-live-container-rosters
Sep 13, 2026
Merged

argszero merged 1 commit into
mainfrom
fix/ui-live-container-rosters

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Two hand-written rosters in ui/js/app.js enumerated the "live" containers by id, and both omitted the admin models table #model-body — which was added later (PR #97) while the rosters date from PR #69/#79:

  1. bindLiveRetry("<id>", fn) (13 entries, bound once at DOMContentLoaded) attached the load-error 重试 button of each container. #model-body was never registered, so renderAdminModels() rendered a 重试 button that did nothing when clicked. The smoking gun is the renderer itself: loadErrorHtml(emptyLabel, retryFn, retryLabel) declared a retryFn parameter and never used it — the renderer was always meant to hand its loader over, and the roster was the only thing wiring it up.
  2. KBD_TABLE_IDS listed the tables reachable by ↑/↓/Enter. #model-body was absent, so its rows could not be activated (no .row-active) while all seven sibling tables could.

The fix removes both rosters instead of extending them — a roster that must be maintained by hand is the defect, not the one missing entry. Both containers are now derived from the DOM, so no future table can be forgotten:

Before After
retry wiring bindLiveRetry(id, fn) ×13 + per-container addEventListener setLiveError(container, html, loader) — the renderer hands over its loader; one-time container delegation (WeakMap/WeakSet) dispatches [data-live-retry]
kbd container KBD_TABLE_IDS.indexOf(tb.id) >= 0 kbdTbodyOf(t) = t.closest("tbody"), via a single document-level click delegation

Runtime evidence (a jsdom probe booting the real index.html + the four scripts, only fetch stubbed so the admin endpoints 500):

check pre-change post-change
#dept-body retry re-requests (control) 5 5
#model-body retry re-requests (test) 0 5
#model-body second retry click 0 5
#dept-body row click activates (control) true true
#model-body row click activates (test) false true
raise-requests rows activate (declared side effect) false true
a run-time-injected table's rows activate (no roster can cover it) false true
verdict DEFECT — 5/7 failed OK — 7/7

Related Issue

None — the repository carries no open issues. Found by reconciling the two enumerated rosters against the tables that actually exist.

Changes

  • ui/js/app.js
    • setLiveError(container, html, loader) replaces the deleted bindLiveRetry; the dead retryFn parameter is dropped from loadErrorHtml. All 12 loadErrorHtml/loadErrorRow call sites now pass their loader through it.
    • KBD_TABLE_IDS deleted; the keyboard container is derived from the event target (kbdTbodyOf = closest("tbody")) and the row/table click delegations are merged into a single document-level listener.
    • kbdRows/kbdContainerFrom treat a container that has been rebuilt (isConnected === false) as "no active table" instead of highlighting rows that are gone.
  • ui/index.html — cache-bust js/app.js?v=20260913-2-3.
  • ui/README.md — the keyboard-nav and degradation-pattern sections named both deleted rosters; they now describe the DOM-derived contract.
  • 涉及配置/数据结构的改动已同步示例文件 — N/A: no config or data-structure change.

Declared side effect (a consequence of "every data table is navigable"): the JS-built raise-requests table now has keyboard row navigation. It was outside the old roster and passed null for the retry callback, so its degraded state had no 重试 button at all; it now reloads through loadAdmin().

Tests

  • cargo test 全部通过 — 190 passed / 0 failed, i.e. unchanged from main (this change is JS + docs only; the count staying put is the evidence that no Rust behaviour moved).
  • cargo fmt --check 通过
  • cargo clippy --all-targets -- -D warnings 通过
  • 新增/更新了单元测试 — N/A: ui/ carries no test harness in-repo (the probes live outside it), and the change's assertion is the jsdom A/B above: with the pre-change app.js the probe is red (5/7), with this branch it is green (7/7), and the #dept-body control passes in both legs.

Checklist

  • 分支命名符合约定 — fix/ui-live-container-rosters
  • Commit message 使用 Conventional Commits 格式 — fix(ui): derive the live containers from the DOM, not from rosters
  • 单一职责,改动最小化 — one defect class (enumerated live containers), no opportunistic refactor; node --check ui/js/app.js clean.

Two hand-written rosters enumerated the "live" containers by id, and both
omitted the admin models table `#model-body`:

1. `bindLiveRetry("<id>", fn)` bound the load-error 重试 button of 13
   containers. `#model-body` was never registered, so `renderAdminModels()`
   rendered a 重试 button that did nothing when clicked. The smoking gun is
   `loadErrorHtml(emptyLabel, retryFn, retryLabel)`: it declared `retryFn` and
   never used it — the renderer was always meant to hand its loader over.

2. `KBD_TABLE_IDS` listed the tables reachable by ↑/↓/Enter. `#model-body`
   was absent, so its rows could not be activated (no `.row-active`).

Both rosters are removed rather than extended — a roster that must be
maintained by hand is the defect, not the missing entry:

- `setLiveError(container, html, loader)` replaces `bindLiveRetry` and the
  unused `retryFn` parameter. Whoever renders the degraded state hands over
  its loader; a one-time, container-level click delegation (WeakMap/WeakSet)
  dispatches `[data-live-retry]` clicks, so a rebuilt container keeps working.
  All 12 `loadErrorHtml`/`loadErrorRow` call sites were converted.
- The keyboard-nav container is derived from the event target
  (`kbdTbodyOf` = `closest("tbody")`) instead of matched against a list of
  ids, via a single document-level click delegation. Any data table is now
  navigable without being registered, and dynamically rebuilt tables keep
  their handler.

Declared side effect: the JS-built raise-requests table gains keyboard row
navigation (it was outside the old roster and its degraded state passed
`null` for the retry callback, so it had no retry button either; it now
reloads via `loadAdmin()`).

Docs: `ui/README.md` described both deleted rosters by name; the two sections
are updated to the DOM-derived contract.

Verified with a jsdom probe booting the real `index.html` + the four scripts
(fetch stubbed, admin endpoints 500): before the change `#model-body` renders
but re-requests 0 times on retry and never activates on click (5/7 checks
red); after, all 7 pass, control `#dept-body` passes in both legs, and a
run-time-injected table's rows activate (no roster can cover it).
`cargo fmt --check`, `cargo clippy --all-targets -- -D warnings` and
`cargo test` (190 passed) all clean; cache-bust bumped to `app.js?v=20260913-3`.
@argszero
argszero merged commit 1e64d98 into main Sep 13, 2026
1 check passed
@argszero
argszero deleted the fix/ui-live-container-rosters branch September 13, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant