v1.5.0-rc.14
[1.5.0-rc.14] — 2026-04-26
Fixed
- Update-eligibility pill never rendered (rc.13 regression). The blocker pills shipped in rc.13 (
Trigger filtered,Below threshold,Maturing, etc.) silently rendered nothing for every container in every view (table rows, side panel, full-page detail, dashboard widget). The backend computed eligibility correctly and serialised it in the API response, but the UI'smapApiContainer()didn't declare or pass through theupdateEligibilityfield — TypeScript's structural typing dropped the unknown JSON property at the boundary. Fix is two-part:- UI mapper —
ApiContainerInputnow declaresupdateEligibility, andmapApiContainerpasses it through with aderiveUpdateEligibilityvalidator that rejects malformed eligibility objects (missingeligibleboolean, missingevaluatedAt) and filters out malformed individual blockers (badreasonstrings, missingmessage). One change lights up every consumer (list, side panel, full-page, dashboard). - Backend SSE enrichment — the store broadcasts raw container objects on
dd:container-added/dd:container-updated, butupdateEligibilityis computed on-demand in the list handler and never persisted. Without enrichment, live SSE patches would deliver containers with eligibility undefined, flickering the pill on every update. Newsse-container-enrichment.tshelper computes eligibility from registry triggers + active operations and attaches it to each lifecycle payload before broadcast. - Reported in #307.
- UI mapper —
- #323 — Popovers on the Containers list rendered off-screen for the last row + drifted on scroll. The "More" actions menu and the column picker both used
position: fixedwith coordinates captured from the trigger'sgetBoundingClientRect()and unconditionally opened below the trigger. When the trigger sat near the bottom of the viewport (e.g. the last container row), the menu was clipped off-screen and inaccessible. The popovers also stayed at their captured pixel coordinates while the table scrolled, drifting away from the trigger. Added a smallbuildPopoverStylehelper that measures available space below vs above and flips the popover upward when there's not enough room below; added a globalscrolllistener (capture phase, to catch internal scroll containers) that closes both popovers since their fixed coordinates can't track a moving anchor. Sibling popovers in this codebase (NotificationBell, etc.) intentionally untouched here — different UX surfaces, separate consideration.
Tests / CI
- Eligibility coverage closed end-to-end. 13 new mapper unit tests (full eligibility roundtrip including
actionHint/liftableAt/details, all 13 validUpdateBlockerReasonvalues, malformed-blocker filtering, missing-field rejection, non-arrayblockershandled as empty list). 7 new SSE enrichment tests (happy path, malformed payloads, error resilience). The 55 existingUpdateEligibilityBadgescomponent tests already covered the render gate. Manual verification: builtdrydock:devwith the fix, ran withDD_ACTION_DOCKER_LOCAL_AUTO=oninclude, confirmed 23 backendtrigger-not-includedblockers render as 23 amber "Trigger filtered" pills in the Kind column. - Popover positioning coverage. 6 new tests in
ContainersView.spec.ts: actions menu and column picker each flip up when the trigger is near viewport bottom; global scroll closes the actions menu, closes the column picker, and is a no-op when nothing is open; scroll listener is removed on unmount. Manual verification: builtdrydock:devwith the fix, scrolled the last container row to the viewport bottom, opened its More menu — the menu rendered fully within the viewport (anchored bottom: 48px from viewport top) instead of clipping off-screen.