Let core's stack accordion drive block visibility - #339
Merged
Merged
Conversation
Stacks render as an accordion that opens one stack and collapses the rest, so half a stacked board was hidden from the first render while every block evaluated and rendered regardless. Under the new gate_stacks option core's board server reads that accordion back, requiring the blocks of every open stack plus every unstacked block and parking the rest. Which stack starts open is derived server-side, since core renders it; paint is reported only once the accordion input has.
nbenn
force-pushed
the
338-collapsed-stacks
branch
from
August 19, 2026 15:40
9cf221e to
b17c535
Compare
Core's front-end now drives the visibility channels the way any other front-end does, through gate_stacks() as board_server()'s default callbacks value. A board driven by something else passes its own callbacks and core tracks nothing, rather than the board server gating unconditionally and racing that front-end on the same slots.
The gate_stacks option was a third switch for something gate_visibility already governs, so it is gone: the callback is simply the default and does nothing until the stack accordion reports, which a board rendering its own UI never does. Newly created stacks now open rather than arriving collapsed, which under the gate would have parked the blocks the user had just grouped.
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
gate_stacksblockr_option()(defaultFALSE) the board server reads the accordion input back, marking the blocks of every open stack plus every unstacked blockrequiredand parking the rest, so collapsing a stack stops its blocks evaluating and rendering while expanding one starts them again.required[[id]](FALSE)rather than an absent slot, so they stay built and re-expanding shows them without a rebuild.<board>_stackstoNS(<board>, "stacks"), which is what lets the board module read the inputbslibhad already wired. Nothing in the stack selects on the old ID (checked across blockr.core / .dock / .ui / .dag / .assistant), and the only other reference in core is theinsertUI()target ininsert_stack_ui(), which moves with it.stack_ui.board()viaaccordion(open =)rather than left to bslib's implicit first-panel default, so what core requires before the client reports comes from the same rule that rendered the markup.visibility$visibleis written only once the accordion input has arrived. Core knows what it asked to be shown before the client says anything, but reporting that as painted would let background construction run against first paint — the propertyrequired_fulfilled()exists to protect.The part that needed measuring
The accordion input reads
NULLin two different situations — before the binding has reported, and once the user has collapsed every stack. Treating them alike would either park every block at startup or keep requiring a collapsed stack's blocks. What separates them is that the input name is registered either way:shiny:::ReactiveValues$set()skips its dedupe check when the key is new, so the first report lands and invalidates readers even when its value isNULL, and"stacks" %in% names(input)isTRUEfrom then on. Measured in a browser with every panel closed at startup, the module seesnames(input) = "stacks"andinput$stacks = NULL.The same probe settled a second question. In a real session the accordion has already reported by the time the module server body runs, so the derived open set matters only for a board whose UI is inserted later, and for
testServer.Testing
Six
testServertests cover what core requires, the expand/collapse transitions, theNULL-versus-unreported distinction, an unstacked board, and the option staying off by default. Against the unfixed tree they fail — including on the bug itself:evaluated("c")andrendered("c")for a block in the collapsed stack come backTRUE.One shinytest2 test covers the seam no mock exercises: that what bslib reports is the panel
data-value, in exactly the form core rebuilds from the stack IDs it holds. It drivesinst/examples/board/gate/app.Rand asserts eval status —breadyandddormanton load,dreachingreadywhen its stack is expanded,bfalling todormantwhen its stack is collapsed.Note for #337
This was first written against #337's claim set, as issue #338 proposes, and then rebased onto
mainto land first. The exercise did answer the question the issue asked — core's UI sits in the claim set on the same footing as a front-end — but the owner label and payload round trip are overhead for an in-core owner, which writes its slots synchronously and needs neither thegate_claimedlatch nor asustaindelta. Migrating this tovisibility$gateplus a claim is a small, mechanical change for #337 to absorb, of the same shape as the dock#417 migration.Fixes #338