7.7 KiB
ScadaBridge UI consistency — first batch review
Baseline accepted G1 pilot commit: cdd3adf6; Theme 1.0.0 published exact package. This batch follows parent G1 acceptance; it does not change package pins, policies, validators, UTC handling, lazy loading or fleet services. Secrets.Ui adoption awaits SEC07 release. No deploys or live writes.
Scope grouped for review
evidence/sb01-03/source-groups.json lists every changed path; source-freeze-sha256.json records the frozen review source. No SB04+ work started.
- Dialog naming/lifecycle: DialogHost title/body/prompt ID relationships; AuditDrilldownDrawer modal/name/Escape; native
dialog.jsper-overlay Map state, actual-event synchronous Tab prevention, filtered focusable children, reference-counted inert siblings, scroll-lock ownership, detached-node cleanup, connected-trigger or heading fallback; component disposal invokes deactivation. Existing Confirm/Prompt/Show result semantics and drawer OnClose are preserved. Generic extraction waits for a second consumer. - Label/ID associations: 126 previously standalone labels associated with native controls across28 files, IDs scoped to component instances; repeated alarm-band keys include the band key. Mutually exclusive numeric/text/Boolean match controls share one relationship safely. Audit multi-select names combine visible field label and current summary; shared table search has a visible label and unique ID. Remaining contextual searches/table cells/move controls receive explicit names without changing bindings/callbacks/conditions.
- Intentional caption conversions:
caption-conversions.jsonenumerates files. Composite schema/editor sections, result payloads, console output, button-group captions and list headings use caption divs instead of labels targeting no labelable element. Existing layout classes and content are retained; no output or parameter behavior changed. - Validation relationships: OPC/Mx existing FieldError selection logic is retained and factored into a helper. Only fields already mapped by RenderFieldError get conditional aria-invalid, describedby and an error ID/alert. Successful removal of Errors removes invalidity and stale description. No new validation rule or validation bypass.
- Tests: existing dialog/drawer/OPC suites extended; new ControlLabelTests checks table search after typing. Strict clipboard test explicitly configures the new lifecycle interop and retains its clipboard payload assertions; no test mode weakening/skips.
Test evidence
red-dialogs.log: two intended missing-name/modal failures. red-labels.log is retained as an invalid initial test fixture (required RowContent omitted); corrected fixture red-labels-corrected-fixture.log fails for the intended missing input ID. red-validation.log retains a compile error from a duplicate attribute found during the broad scan, then corrected; red-validation-corrected.log shows the intended missing aria-invalid failure. Duplicate ParkedMessages attribute was removed; its existing contextual accessible label remains unchanged.
Focused related suite: green-batch.log104 passed. Intermediate full suite: full-suite.log1554 passed/zero skipped before the last validation regression and source naming completion. Final frozen-source suite passed 1555/1555, zero failures/skips (final-suite.log, sb01-03-final.trx).
Browser pilot and remaining gates
Disposable new fixture source is retained in umbrella components/ui-theme/validation/scadabridge-ui/host; actual source RCL ProjectReference, anonymous/synthetic local services only. It binds127.0.0.1:5201, separate from retained G1 canary5199. Existing rigs untouched. infra/tools/ui_consistency_browser.py accepts explicit loopback base URL/storage-state/output directory; no real writes.
First matrix and one confirmation are retained separately under browser/ and browser-confirmation/. Both passed name/prompt linkage, synchronous first/last Tab and Shift+Tab, hidden/disabled filtering, inert background, Escape, normal trigger focus return, lock cleanup, repeat opening and no-focusable containment at1440/390. Disconnected-trigger fallback failed: the native code focused the heading, then immediately removing its temporary tabindex made Chromium blur to BODY. Source now restores that attribute only after blur. No third broad screenshot/polish loop is claimed; A focused no-screenshot technical probe (focused-fallback.json) now confirms all three actual hosts restore the disconnected trigger to h1, retain tabindex while focused, clear it on blur, and release locks/inert. Parent lifecycle review remains required.
SB03 source inventory does not prove every composite editor's internal accessible name or field help/error relationship at runtime. Remaining work before calling SB03 complete: machine-check frozen associations, contextual duplicate IDs on repeated editors, all existing form help relationships, Monaco composite context, and rendered browser/assistive-tree samples for transport/notification/instance forms. Aggregate form errors must stay aggregate rather than falsely marking all fields invalid. These are explicit pending acceptance items, not covered by passing unit tests.
Lifecycle review refinement
The supported policy is one active overlay, including the audit drawer and global DialogHost. No arbitrary nested or sibling overlay support is implied by the internal Map/ref-count bookkeeping. Native activation rejects any different active overlay or inert target. C# cancels a rejected service dialog through its unchanged typed cancellation path, or invokes the drawer's existing OnClose callback. DialogService's original second-dialog guard is retained. This prevents descendant Escape bubbling from closing two simultaneously active hosts.
A disposed DialogHost unsubscribes, cancels any pending local result, and deactivates its presentation. No new busy-operation semantics are introduced: confirm merely resolves the caller's result; domain work remains owned by the caller after the dialog closes. The adapter does not cancel or retry domain work. Source scan found no remaining callers of removed captureActiveElement/restoreActiveElement/focusTrap globals in src/tests. Browser evidence for the single-active rejection is single-active-overlay.json; it programmatically attempts a second local presentation while the real drawer is active, verifies cancellation/one remaining drawer, then verifies one Escape closes it and restores its trigger.
Final lifecycle-refined full suite: 1556/1556 passed, zero skips/failures, reviewed-suite.log / sb01-03-reviewed.trx. This supersedes1555 for the additional rejection/disposal regression. No first-batch commit until parent source/lifecycle review accepts the frozen diff. Rollback reverts this batch only; accepted G1 package/palette pilot is separate.
Peer finding closure and shared patch adoption
Native focus restoration now verifies document.activeElement after attempting the trigger; still-connected disabled/hidden/unfocusable triggers use heading fallback too. unusable-trigger-red.json preserves the reproduced disabled/hidden failures, unusable-trigger-green.json verifies all four target conditions and clean lock/inert/tabindex restoration. The retained Python test includes a span made unfocusable while still connected, not merely a replacement/disconnection.
Published Theme1.0.1 is restored from the official Gitea index into task-local /tmp/scadabridge-theme101-packages. Package SHA256995b117b0245577598c91404ed7957c071ebe3413696c8be48b419c9deb1bd5d and runtime DLL parity are in theme101-provenance.json; no stale default cache accepted. Full suite on the patch:1557/1557 passed, zero skips/failures. The added contextual TreeView checkbox regression remains SB03 work; its red failure is retained.