Secrets.Ui delete/confirm modal is invisible on every Bootstrap host — bare .modal class collides with Bootstrap's display:none, and the RCL ships no CSS
#2
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found 2026-07-19 while live-verifying the ScadaBridge#22 fix (the row Delete button appeared dead). Distinct from both
lmxopcua#483(render mode) andScadaBridge#22(missing audit seam) — this one is in the Secrets.Ui library itself.Symptom
Clicking a secret row's Delete button does nothing visible. The circuit is alive and
ShowDeleteruns —[data-testid="delete-modal"]attaches to the DOM — but the modal never appears:Reproduced on ScadaBridge Central (localhost:9001) and OtOpcUa AdminUI (localhost:9200). HistorianGateway links the same Bootstrap bundle and is expected identical. MxGateway ships no Bootstrap and is the only host where the bare markup can render.
Cause
ConfirmDeleteModal.razorrenders<div class="modal" ...>andZB.MOM.WW.Secrets.Uiships no CSS at all (no wwwroot css, no scoped.razor.css). The component silently depends on the host having no opinion about.modal— but Bootstrap 5 (linked by OtOpcUa, HistorianGateway, and ScadaBridge's Host wwwroot) defines.modal { display: none; }and only shows it when its own JS adds.show. The RCL's modal is pure Blazor (@if (Visible)) and never gets Bootstrap's class, so it renders permanently hidden.modal-backdrop-inpage/modal-card/btn-roware similarly unstyled.The defect was invisible to every prior check: the family sweep's Playwright gate only exercised Add secret (the in-page
SecretEditor, which uses no modal), and the modal's@onclickhandlers are fully functional — forcingel.style.display='block'makes the whole delete flow work (that is how the probe secrets were cleaned up).Suggested fix (in
ZB.MOM.WW.Secrets.Ui)zb-secrets-modal,zb-secrets-modal-backdrop,zb-secrets-modal-card) so no host framework can collide.ConfirmDeleteModal.razor.css(bundles into the consumer's*.styles.cssautomatically) covering overlay positioning, backdrop, and card, consistent with the Theme tokens.display != none), on a host page that links Bootstrap — the collision only reproduces there.Verification recipe
On any Bootstrap host (e.g. OtOpcUa docker-dev :9200): create a secret, click its row Delete, assert
[data-testid="delete-modal"]is visible and[data-testid="confirm-delete"]is clickable without JS style forcing. Rig states: both rig probe secrets (tmp/modal-probeon :9200,sb22/audit-bridge-proofon :9001) were force-deleted through the realConfirmAsyncpath, so stores are clean.FIXED in Secrets.Ui
0.2.3(scadaproj main @ 62ab8f6; 5 packages published to the Gitea feed 2026-07-19).Fix, entirely inside the RCL so a package bump repairs every host with no host-side changes:
zb-secrets-modal/-backdrop/-card/-title— no host framework selector can match the elements;<style>block alongside the markup (Theme tokens with fallbacks). Scoped.razor.csswas deliberately rejected: only ScadaBridge links a scoped-CSS bundle — OtOpcUa, HistorianGateway and MxGateway never link{App}.styles.css, so isolation CSS would silently fail to load on 3 of 4 hosts, recreating the exact defect class;data-testids unchanged, so existing gates keep their selectors.Regression pins (bUnit): no Bootstrap-reserved class name in the rendered markup, and the component ships a style block that both positions the overlay and gives it a display mode. Suite 182 pass / 0 fail.
Live-verified on a Bootstrap host (ScadaBridge local cluster, bumped to
0.2.3@63510778): created a probe secret, clicked row Delete —[data-testid="delete-modal"]now VISIBLE withcomputed display=flex,class="zb-secrets-modal", zero JS style forcing — confirmed through the realConfirmAsync, row gone, store clean. Renders as a proper centered card over a dimmed backdrop.Remaining rollout: OtOpcUa (
0.2.2), HistorianGateway and MxGateway (0.2.1) pick the fix up on their next Secrets bump.Rollout complete 2026-07-19 — all four apps on
0.2.3, pushed:63510778lmxopcua master @ be87ddebhistoriangw main @ 7452eb4mxaccessgw main @ 8f7ee49Gate = create probe secret → row Delete →
delete-modalvisible (display=flex,zb-secrets-modal) with zero JS forcing → confirm → row gone, store clean.Side fix while verifying: HG's whole test suite had been unbuildable under warnings-as-errors from the pre-existing AngleSharp NU1902 (transitive via bunit) — fixed with a test-only pin to 1.5.2 (
historiangw 6bc005d; note 1.3.1 is still in the advisory range).