Skip to content

Let core's stack accordion drive block visibility - #339

Merged
nbenn merged 3 commits into
mainfrom
338-collapsed-stacks
Aug 20, 2026
Merged

nbenn merged 3 commits into
mainfrom
338-collapsed-stacks

Conversation

@nbenn

@nbenn nbenn commented Aug 19, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Core's board UI becomes a consumer of its own visibility channels. Under a new gate_stacks blockr_option() (default FALSE) the board server reads the accordion input back, marking the blocks of every open stack plus every unstacked block required and parking the rest, so collapsing a stack stops its blocks evaluating and rendering while expanding one starts them again.
  • Parked rather than dropped: a collapsed stack's blocks get required[[id]](FALSE) rather than an absent slot, so they stay built and re-expanding shows them without a rebuild.
  • The option defaults off because turning it on is a behaviour change — expanding a stack starts the computation rather than revealing a finished one — and because the accordion is core's own front-end, so a board already driven by blockr.dock would be writing the same channel from two places.
  • The accordion container ID moves from <board>_stacks to NS(<board>, "stacks"), which is what lets the board module read the input bslib had 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 the insertUI() target in insert_stack_ui(), which moves with it.
  • Which stack starts open is now stated in stack_ui.board() via accordion(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.
  • Paint deliberately does not lead demand: visibility$visible is 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 property required_fulfilled() exists to protect.

The part that needed measuring

The accordion input reads NULL in 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 is NULL, and "stacks" %in% names(input) is TRUE from then on. Measured in a browser with every panel closed at startup, the module sees names(input) = "stacks" and input$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 testServer tests cover what core requires, the expand/collapse transitions, the NULL-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") and rendered("c") for a block in the collapsed stack come back TRUE.

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 drives inst/examples/board/gate/app.R and asserts eval status — b ready and d dormant on load, d reaching ready when its stack is expanded, b falling to dormant when its stack is collapsed.

Note for #337

This was first written against #337's claim set, as issue #338 proposes, and then rebased onto main to 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 the gate_claimed latch nor a sustain delta. Migrating this to visibility$gate plus a claim is a small, mechanical change for #337 to absorb, of the same shape as the dock#417 migration.

Fixes #338

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
nbenn changed the base branch from 321-unified-demand to main August 19, 2026 15:40
@nbenn
nbenn force-pushed the 338-collapsed-stacks branch from 9cf221e to b17c535 Compare August 19, 2026 15:40
nbenn added 2 commits August 19, 2026 16:21
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

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
R/block-server.R 96.64% <ø> (ø)
R/board-server.R 96.81% <ø> (+0.79%) ⬆️
R/stack-gate.R 100.00% <100.00%> (ø)
R/stack-ui.R 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nbenn
nbenn marked this pull request as ready for review August 20, 2026 12:18
@nbenn
nbenn added this pull request to the merge queue Aug 20, 2026
Merged via the queue into main with commit 0fcb65d Aug 20, 2026
19 of 20 checks passed
@nbenn
nbenn deleted the 338-collapsed-stacks branch August 20, 2026 12:37
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.

Blocks in a collapsed stack still evaluate and render

1 participant